解决 jps/jconsole NullPointerException 的问题

前阵子windows设置环境变量的时候卡死了,强制结束系统进程,结果导致用户环境变量全丢了,当时也没在意,后来使用jconsole的时候,问题来了,打开就报NPE空指针异常,无法调出选择连接的那个面板。网上搜了一下,发现有人使用JPS的时候,也报一样的错:

Exception in thread "main" java.lang.NullPointerException
at sun.jvmstat.perfdata.monitor.protocol.local.LocalVmManager.activeVms(LocalVmManager.java:148)
at sun.jvmstat.perfdata.monitor.protocol.local.MonitoredHostProvider.activeVms(MonitoredHostProvider.java:150)
at sun.tools.jps.Jps.main(Jps.java:62)

再搜了一下,有人给Java提交过一个bug,显示已经解决了

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
        at sun.jvmstat.perfdata.monitor.protocol.local.LocalVmManager.activeVms(LocalVmManager.java:127)
        at sun.jvmstat.perfdata.monitor.protocol.local.MonitoredHostProvider.activeVms(MonitoredHostProvider.java:133)
        at sun.tools.jconsole.ConnectDialog$ManagedVmTableModel.getManagedVirtualMachines(ConnectDialog.java:528)
        at sun.tools.jconsole.ConnectDialog$ManagedVmTableModel.refresh(ConnectDialog.java:511)
        at sun.tools.jconsole.ConnectDialog$ManagedVmTableModel.<init>(ConnectDialog.java:502)
        at sun.tools.jconsole.ConnectDialog.<init>(ConnectDialog.java:139)
        at sun.tools.jconsole.JConsole.showConnectDialog(JConsole.java:571)
        at sun.tools.jconsole.JConsole.access$100(JConsole.java:34)
        at sun.tools.jconsole.JConsole$4.run(JConsole.java:702)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:461)
        at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:242)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)

这个bug详情很有用,看了这个大概知道是由于临时文件夹的问题引起的,echo %TMP%,发现是有路径的,start %TMP%,提示没权限。于是用管理员身份运行jconsole,一切正常了。然后修改用户环境变量,指向有权限的文件夹:C:\Users\xxx\AppData\Local\Temp,然后jconsole和jps终于可以正常使用了。

看来这个bug在后来的某个版本又被谁改回去了,应该再去Oracle把这个bug reopen一下。

mysql jdbcURL的autoReconnect

mysql JDBC URL格式如下:

jdbc:mysql://[host:port],[host:port]…/[database][?参数名1][=参数值1][&参数名2][=参数值2]…

现只列举几个重要的参数,如下表所示:

参数名称 参数说明 缺省值 最低版本要求
user 数据库用户名(用于连接数据库)
所有版本
password 用户密码(用于连接数据库)
所有版本
useUnicode 是否使用Unicode字符集,如果参数characterEncoding设置为gb2312或gbk,本参数值必须设置为true false 1.1g
characterEncoding 当useUnicode设置为true时,指定字符编码。比如可设置为gb2312或gbk false 1.1g
autoReconnect 当数据库连接异常中断时,是否自动重新连接? false 1.1
autoReconnectForPools 是否使用针对数据库连接池的重连策略 false 3.1.3
failOverReadOnly 自动重连成功后,连接是否设置为只读? true 3.0.12
maxReconnects autoReconnect设置为true时,重试连接的次数 3 1.1
initialTimeout autoReconnect设置为true时,两次重连之间的时间间隔,单位:秒 2 1.1
connectTimeout 和数据库服务器建立socket连接时的超时,单位:毫秒。 0表示永不超时,适用于JDK 1.4及更高版本 0 3.0.1
socketTimeout socket操作(读写)超时,单位:毫秒。 0表示永不超时 0 3.0.1

对应中文环境,通常mysql连接URL可以设置为:

jdbc:mysql://localhost:3306/test?user=root&password=&useUnicode=true&characterEncoding=gbk&autoReconnect=true&failOverReadOnly=false

在使用数据库连接池的情况下,最好设置如下两个参数:

autoReconnect=true&failOverReadOnly=false

需要注意的是,在xml配置文件中,url中的&符号需要转义成&。比如在tomcat的server.xml中配置数据库连接池时,mysql jdbc url样例如下:

jdbc:mysql://localhost:3306/test?user=root&amp;password=&amp;useUnicode=true&amp;characterEncoding=gbk

&amp;autoReconnect=true&amp;failOverReadOnly=false

完整参数可参考官方文档:http://dev.mysql.com/doc/connector-j/en/connector-j-reference-configuration-properties.html

Spring websocket

Spring自从4.0开始提供了对websocket的支持,配合sockjs,可以部分兼容到IE6,websocket终于可以大行其道了。

实际使用中遇到不少问题,逐步列举出来,避免以后忘掉。

  1. 由于浏览器设置了http代理,结果创建websocket时失败,提示:Error in connection establishment: net::ERR_TUNNEL_CONNECTION_FAILED。取消浏览器代理后恢复正常。
  2. 在web.xml中,DispatcherServlet和spring-mvc需要用到的全部filter,都需要加上<async-supported>true</async-supported>,以便在sockjs兼容不支持websocket的浏览器时使用。
  3. 工程使用spring-mvc传统的方式配置,即使用ContextLoaderListener来加载root的Spring-contextConfigLocation(除Controller外的其他Component),使用DispatcherServlet来加载Controller。而处理websocket的业务逻辑写在Controller中,加上了@MessageMapping的注解,结果请求根本到不了Controller里面去。后来发现AbstractMethodMessageHandler负责扫描@MessageMapping,但是在该类的子类实例化时,把Spring的Root Context扔进去了,这里面是没有Controller的类的,所以后面websocket的请求就到不了Controller。将websocket的相关配置文件放到DispatcherServlet里去加载,于是问题解决。

相关配置示例:

web.xml:

<context-param>  
        <param-name>contextConfigLocation</param-name>  
        <param-value>classpath*:/applicationContext*.xml</param-value>  
    </context-param>  
  
<listener>  
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    </listener>  
  
<servlet>  
        <servlet-name>SpringMVC</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>  
                classpath*:/spring-*.xml  
            </param-value>  
        </init-param>  
        <load-on-startup>1</load-on-startup>  
        <async-supported>true</async-supported>  
</servlet>

applicationContext.xml:

<context:component-scan base-package="xxx.xxx">  
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>  
</context:component-scan>

spring-mvc.xml:

<context:component-scan base-package="x.x.x" use-default-filters="false">  
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>  
</context:component-scan>

spring-websocket.xml:

<websocket:message-broker application-destination-prefix="/app">  
        <websocket:stomp-endpoint path="/chat">  
            <websocket:sockjs/>  
        </websocket:stomp-endpoint>  
        <websocket:simple-broker prefix="/public,/private"/>  
</websocket:message-broker>

Sizing the Java heap

Sizing the Java heap

Size your Java heap so that your application runs with a minimum heap usage of 40%, and a maximum heap usage of 70%.

IntroductionAn incorrectly sized Java heap can lead to OutOfMemoryError exceptions or to a reduction in the performance of the Java application.

If the Java heap is smaller than the memory requirements of the application, OutOfMemoryError exceptions are generated because of Java heap exhaustion. If the Java heap is slightly larger than the requirements of the application, garbage collection runs very frequently and affects the performance of the application.

You must correctly size the Java heap based on the real memory usage of the Java application.

 

Sizing the heap based on application memory utilizationSet the maximum Java heap size, using the -Xmx command-line option, to a value that allows the application to run with 70% occupancy of the Java heap.

The Java heap occupancy often varies over time as the load applied to the application varies. For applications where occupancy varies, set the maximum Java heap size so that there is 70% occupancy at the highest point, and set the minimum heap size, using the -Xms command line option, so that the Java heap is 40% occupied at its lowest memory usage. If these values are set, the Java memory management algortihms can modify the heap size over time according to the application load, while maintaining usage in the optimal area of between 40% and 70% occupancy.

Maximum possible heap size and maximum recommended heap size (32-bit Java)The memory space provided by the operating system to the Java process varies by operating system and is used for two separate memory areas: the Java heap and the native heap. Because a finite amount of memory is provided by the operating system, and that memory is shared between the two heaps, the larger the amount of memory that is allocated to the Java heap, using the -Xmx setting, the smaller the native heap becomes. If the native heap is too small, an OutOfMemoryError occurs when it is exhausted, in the same way as for the Java heap.

Platform Additional options Maximum heap size Recommended heap size limit Additional notes
AIX None 3.25 GB 2.5 GB Maximum heap size is not required to be, but should ideally be, a multiple of 256 MB
Linux None 2 GB 1.5 GB
Hugemem Kernel 3 GB 2.5 GB
Windows None 1.8 GB 1.5 GB
/3GB 1.8 GB 1.8 GB

The table shows both the maximum Java heap possible and a recommended limit for the maximum Java heap size setting. The use of a maximum Java heap setting up to the recommended limit is unlikely to reduce the native heap size to a point that the native heap is exhausted.

Before setting a Java heap size greater than the recommended limit, you must understand the level of usage of the native heap, to ensure that the native heap does not become exhausted.

If you are running an application that has particularly high numbers of threads or makes heavy use of Java Native Interface (JNI) code, for example, Type 2 JDBC drivers, you might experience problems relating to the native heap with smaller Java heap sizes than the recommended limit.

Maximum possible heap size and maximum recommended heap size (64-bit Java)When running 64-bit Java, the memory space provided by the operating system to the Java process is very large. You can therefore assume that no limit is imposed on the maximum size of the Java heap because of the contention of memory resource between the Java heap and the native heap.

Java heap size and amount of physical memory availableIt is important to have more physical memory than is required by all of the processes on the machine combined to prevent paging or swapping. Paging reduces the performance of the system and affects the performance of the Java memory management system.

When increasing the Java heap size, ensure that enough unused physical memory is available on the machine to cover the increase. If sufficient physical memory is not available, either install additional memory or take into account the effect on overall performance that occurs.

This requirement does not apply to operating systems running on System z.

Size your Java heap so that your application runs with a minimum heap usage of 40%, and a maximum heap usage of 70%.

Spring命名空间之AOP

Spring加载时,会使用ClassLoader去查找所有能找到的”META-INF/spring.handlers”文件,并存放在handlerMappings中(DefaultNamespaceHandlerResolver在干这事),遇到除beans外的Namespace,就会去这里查找对应的解析器,如果不存在就报错,存在就使用相应的解析器进行解析。

<aop>是由AopNamespaceHandler来进行解析的。

AspectJAutoProxyBeanDefinitionParser会注册3个类来处理解析任务,分别对应为:

“config”, new ConfigBeanDefinitionParser();

“aspectj-autoproxy”, new AspectJAutoProxyBeanDefinitionParser();

“scoped-proxy”, new ScopedProxyBeanDefinitionDecorator();

要使用aop命名空间,需要在spring的xml中加入:

xmlns:aop="http://www.springframework.org/schema/aop"  
  
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-xsd"

这样便可以在该xml中使用<aop …/>了。

<aop:aspectj-autoproxy/>

使用该配置后,AspectJAutoProxyBeanDefinitionParser会注册AnnotationAwareAspectJAutoProxyCreator到Spring管理的bean中。由于AnnotationAwareAspectJAutoProxyCreator是BeanPostProcessor,所以在所有bean初始化完成后,都会调用该类中的postProcessAfterInitialization来进行处理。

AnnotationAwareAspectJAutoProxyCreator使用findEligibleAdvisors来查找是否有bean对应的Advisors(注册在当前beanFactory中的实现了Advisor接口的类,例如BeanFactoryTransactionAttributeSourceAdvisor),如果有,创建一个该bean的代理(AbstractAutoProxyCreator)。

如果使用了<tx:annotation-driven/>,其中的mode属性默认为proxy,解析到这里的时候,AnnotationDrivenBeanDefinitionParser会自动调用AopNamespaceUtils->AopConfigUtils,而AopConfigUtils会把AnnotationAwareAspectJAutoProxyCreator注册到beanFactory里面去,也就是相当于写了个<aop:aspectj-autoproxy/>的标签。

如果不知道程序应该使用Spring-AOP还是AspectJ,这儿有较好的描述:

http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/aop.html#aop-choosing

Spring-AOP默认使用JDKProxy来代理实现了任何接口的类,如果没有实现任何接口,则使用Cglib2AopProxy来创建代理,对原类的方法调用时,会根据代理类设置的Callback[]进行相应处理。

Spring-AOP默认设置的Callback有

DynamicAdvisedInterceptor(拦截方法调用,检查需要使用的拦截器并执行,例如TransactionInterceptor)

DynamicUnadvisedInterceptor/StaticUnadvisedInterceptor

StaticDispatcher

AdvisedDispatcher(不知道拿来干嘛的)

EqualsInterceptor

HashCodeInterceptor

SerializableNoOp(貌似没啥用?)