Java Class文件格式解析

一、Java Class文件是什么

java语言是跨平台的,所谓一次编写,到处运行。之所以是跨平台的,就是java定义了一套与操作系统,硬件无关的字节码格式,这个字节码就是用java class文件来表示的,java class文件内部定义了虚拟机可以识别的字节码格式,这个格式是平台无关性的,在linux系统或者在windows系统上都是一致的。这个就好比html文件,我们定义好规范,这个系统只要去按照规范显示出来里面的内容就好了。好比html就是class文件,浏览器就是虚拟机一样,通过浏览器去执行html的渲染过程,我们无论是用手机,Windows系统,苹果系统上网,显示出来的内容都是一样。 java虚拟机可以从class文件中加载预定义的字节码,也可以从网络,数据库,消息文件中加载字节码。

  二、Java Class文件的格式

1.多个字节的数据采用大端存储

2.class文件采用定义u1、u2、u4来表示无符号的1、2、4字节数据。

java class
阅读详细 »

Java6,7,8中的String.intern() – 字符串常量池

本文是由http://java-performance.info/string-intern-in-java-6-7-8/翻译而来,如有错误请指正

—————Ranger的分割线—————

这篇文章主要讨论的是String.intern()方法在java6中的实现以及在java7、8中做了哪些改变

字符串常量池(又名字符串标准化)是用一个共享的String对象来替代很多拥有相同值但是不同含义的字符串对象。你能通过自定义的Map<String, String>(根据需要实现软引用或弱引用)并且使用map中的值作为标准值来实现这一目标。或者你可以使用JDK提供的String.intern()方法

有时很多标准是禁止在java6中使用String.intern(),因为如果不受控制的使用池会有可能会照成OutOfMemoryException。常量池在java7中的实现有所改变。在下面的网址可以看到详细介绍:http://bugs.sun.com/view_bug.do?bug_id=6962931 and http://bugs.sun.com/view_bug.do?bug_id=6962930.

Java6中的String.intern()

在美好的过去,所有的被intern的String被存储在PermGen区——虚拟机中一块固定大小的区域,主要用来存放加载的类和字符串常量池。除了被明确intern的字符串,PermGen区的字符串常量池也包含程序中之前使用的字符串(这里要注意是使用过的,如果一个类或方法从没有被加载或调用,任何定义的常量都不会被加载)

最严重的问题是在java6中字符串常量池是放在PermGen区。PermGen区有固定的大小并且在运行时不能被扩展。你可以使用-XX:MaxPermSize=N 来设置。据我所知,不同平台中默认的PermGen区大小是在32M到96M之间。你能增加它的大小,但是它的大小仍然是固定的。这种限制要求你必须小心地使用String.intern(),你最好不用这个方法intern任何不受控的用户输入。这就是为什么在java6中通过手动管理map来实现字符串常量池。

java7中的String.intern()

在java7中Oracel的工程师们对字符串常量池逻辑做了一个非常重要的改变——字符串常量池被迁移到堆中。这意味着你不用再受固定大小的内存限制。现在所有的字符串像其他普通对象一样被放置到了堆中,这样你调优的时候只需要管理堆的大小。在技术上,仅此一点就有足够的理由让我们重新考虑在java7中使用Strng.intern()。但是还有其他的原因。
阅读详细 »

JVM的一些概念

数据类型

Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代表的值就是数值本身;而引用类型的变量保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。
基本类型包括:byte,short,int,long,char,float,double,Boolean,returnAddress
引用类型包括:类类型,接口类型和数组。

堆与栈

堆和栈是程序运行的关键,很有必要把他们的关系说清楚。

heap&stack

栈是运行时的单位,而堆是存储的单位。

栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
在Java中一个线程就会相应有一个线程栈与之对应,这点很容易理解,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。而堆则是所有线程共享的。栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状态、方法返回值等等;而堆只负责存储对象信息。

为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗?

第一,从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据。这样分开,使得处理逻辑更为清晰。分而治之的思想。这种隔离、模块化的思想在软件设计的方方面面都有体现。
第二,堆与栈的分离,使得堆中的内容可以被多个栈共享(也可以理解为多个线程访问同一个对象)。这种共享的收益是很多的。一方面这种共享提供了一种有效的数据交互方式(如:共享内存),另一方面,堆中的共享常量和缓存可以被所有栈访问,节省了空间。
第三,栈因为运行时的需要,比如保存系统运行的上下文,需要进行地址段的划分。由于栈只能向上增长,因此就会限制住栈存储内容的能力。而堆不同,堆中的对象是可以根据需要动态增长的,因此栈和堆的拆分,使得动态增长成为可能,相应栈中只需记录堆中的一个地址即可。
第四,面向对象就是堆和栈的完美结合。其实,面向对象方式的程序与以前结构化的程序在执行上没有任何区别。但是,面向对象的引入,使得对待问题的思考方式发生了改变,而更接近于自然方式的思考。当我们把对象拆开,你会发现,对象的属性其实就是数据,存放在堆中;而对象的行为(方法),就是运行逻辑,放在栈中。我们在编写对象的时候,其实即编写了数据结构,也编写的处理数据的逻辑。不得不承认,面向对象的设计,确实很美。 阅读详细 »

垃圾收集器选择

JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行判断。

吞吐量优先的并行收集器

如上文所述,并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。

典型配置:

java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。 -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。 java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC

-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100

-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy

-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

响应时间优先的并发收集器

如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。

典型配置:

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

-XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。

-XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。 -XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

JVM致命错误日志(hs_err_pid.log)解读

致命错误出现的时候,JVM生成了hs_err_pid<pid>.log这样的文件,其中往往包含了虚拟机崩溃原因的重要信息。默认情况下文件是创建在工作目录下的(如果没权限创建的话JVM 会尝试把文件写到/tmp这样的临时目录下面去),当然,文件格式和路径也可以通过参数指定,比如:

java -XX:ErrorFile=/var/log/java/java_error%p.log
这个文件将包括:

  • 触发致命错误的操作异常或者信号;
  • 版本和配置信息;
  • 触发致命异常的线程详细信息和线程栈;
  • 当前运行的线程列表和它们的状态;
  • 堆的总括信息;
  • 加载的本地库;
  • 命令行参数;
  • 环境变量;
  • 操作系统CPU的详细信息。

首先,看到的是对问题的概要介绍:

[java][/java]

# SIGSEGV (0xb) at pc=0x03568cf4, pid=16819, tid=3073346448

[java][/java]

一个非预期的错误被JRE检测到,其中:

SIGSEGV是信号名称
0xb是信号码
pc=0x03568cf4指的是程序计数器的值
pid=16819是进程号
tid=3073346448是线程号
如果你对JVM有了解,应该不会对这些东西陌生。

接下来是JRE和JVM的版本信息:

# JRE version: 6.0_32-b05

# Java VM: Java HotSpot(TM) Server VM (20.7-b02 mixed mode linux-x86 )
运行在mixed模式下。

然后是问题帧的信息:

# Problematic frame:

# C [libgtk-x11-2.0.so.0+0x19fcf4] __float128+0x19fcf4
C:帧类型为本地帧,帧的类型包括:
C:本地C帧
j:解释的Java帧
V:虚拟机帧
v:虚拟机生成的存根栈帧
J:其他帧类型,包括编译后的Java帧
libgtk-x11-2.0.so.0+0x19fcf4:和程序计数器(pc)表达的含义一样,但是用的是本地so库+偏移量的方式。
接下去第一部分是线程信息:

Current thread (0x09f30c00): JavaThread “main” [_thread_in_native, id=16822, stack(0xb72a8000,0xb72f9000)]
当前线程的:

0x09f30c00:指针
JavaThread:线程类型,可能的类型包括:
JavaThread
VMThread
CompilerThread
GCTaskThread
WatcherThread
ConcurrentMarkSweepThread
main:名字
_thread_in_native:线程当前状态,状态枚举包括:
_thread_uninitialized:线程还没有创建,它只在内存原因崩溃的时候才出现
_thread_new:线程已经被创建,但是还没有启动
_thread_in_native:线程正在执行本地代码,一般这种情况很可能是本地代码有问题
_thread_in_vm:线程正在执行虚拟机代码
_thread_in_Java:线程正在执行解释或者编译后的Java代码
_thread_blocked:线程处于阻塞状态
…_trans:以_trans结尾,线程正处于要切换到其它状态的中间状态
id=16822:线程ID
0xb72a8000,0xb72f9000:栈区间
siginfo:si_signo=SIGSEGV: si_errno=0, si_code=1 (SEGV_MAPERR), si_addr=0×00000010
这部分是导致虚拟机终止的非预期的信号信息,含义前面已经大致提到过了。其中si_errno和si_code是Linux下用来鉴别异常的,Windows下是一个ExceptionCode。

EAX=0×00000000, EBX=0x0375dd84, ECX=0×00000000, EDX=0×00000000
ESP=0xb72f0fa0, EBP=0xb72f0fb8, ESI=0×00000000, EDI=0x0a6c1800
EIP=0x03568cf4, EFLAGS=0×00010246, CR2=0×00000010
这是寄存器上下文。

Top of Stack: (sp=0xb72f0fa0)
0xb72f0fa0: 00000000 00402250 0040217f 0375dd84
0xb72f0fb0: 00000000 0a6c1800 b72f0fe8 0356c2c0
0xb72f0fc0: 00000000 0a6c1800 b72f0fe8 003b3e77
0xb72f0fd0: 003e6c8b 0a1a70d0 0a193358 0375dd84
0xb72f0fe0: 0a276418 0a276418 b72f1048 03536c56
0xb72f0ff0: 0acad000 0b3ca978 0000000c 00dd0674
0xb72f1000: 00000003 0a2c7d50 b72f1038 0000330c
0xb72f1010: ffffffff ffffffff 00000001 00000001

Instructions: (pc=0x03568cf4)
0x03568cd4: 89 14 24 89 75 f8 89 d6 89 7d fc 89 c7 e8 7e 1b
0x03568ce4: ea ff 89 34 24 89 87 d4 02 00 00 e8 30 00 ea ff
0x03568cf4: 8b 40 10 89 3c 24 c7 44 24 08 00 00 00 00 89 87
0x03568d04: d0 02 00 00 8b 83 88 24 00 00 89 44 24 04 e8 dd
栈顶程序计数器旁的操作码,它们可以被反汇编成系统崩溃前执行的指令。

Register to memory mapping:

EAX=0×00000000 is an unknown value
EBX=0x0375dd84: <offset 0x394d84> in /usr/lib/libgtk-x11-2.0.so.0 at 0x033c9000
ECX=0×00000000 is an unknown value
EDX=0×00000000 is an unknown value
ESP=0xb72f0fa0 is pointing into the stack for thread: 0x09f30c00
EBP=0xb72f0fb8 is pointing into the stack for thread: 0x09f30c00
ESI=0×00000000 is an unknown value
EDI=0x0a6c1800 is an unknown value
寄存器和内存映射信息。

Stack: [0xb72a8000,0xb72f9000], sp=0xb72f0fa0, free space=291k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C [libgtk-x11-2.0.so.0+0x19fcf4] __float128+0x19fcf4
C [libgtk-x11-2.0.so.0+0x1a32c0] __float128+0xc0
… …
C [libswt-pi-gtk-3738.so+0x33f6a] Java_org_eclipse_swt_internal_gtk_OS__1Call+0xf
J org.eclipse.swt.internal.gtk.OS._Call(III)I
J org.eclipse.swt.internal.gtk.OS.Call(III)I

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
J org.eclipse.swt.internal.gtk.OS._Call(III)I
J org.eclipse.swt.internal.gtk.OS.Call(III)I
j org.eclipse.swt.widgets.Widget.fixedSizeAllocateProc(II)I+5
j org.eclipse.swt.widgets.Display.fixedSizeAllocateProc(II)I+17
v ~StubRoutines::call_stub
线程栈。包含了地址、栈顶、栈计数器和线程尚未使用的栈信息,由于栈可能非常长,打印的长度有限制,但是至少本地栈和Java栈都打印出来了(很多时候本地栈打印不出来,但是Java栈一般都能打印出来)。从中可以看到,Eclipse的虚拟机崩溃了。

ava Threads: ( => current thread )
0x0b4c1000 JavaThread “Worker-247″ [_thread_blocked, id=25417, stack(0x741bc000,0x7420d000)]
0x0a300c00 JavaThread “Worker-246″ [_thread_blocked, id=25235, stack(0x7d30c000,0x7d35d000)]
… …
线程信息。一目了然,不解释了。

VM state:not at safepoint (normal execution)
虚拟机状态。包括:

not at a safepoint:正常运行状态;
at safepoint:所有线程都因为虚拟机等待状态而阻塞,等待一个虚拟机操作完成;
synchronizing:一个特殊的虚拟机操作,要求虚拟机内的其它线程保持等待状态。
VM Mutex/Monitor currently owned by a thread: None
虚拟机的Mutex和Monitor目前没有被线程持有。Mutex是虚拟机内部的锁,而Monitor则关联到了Java对象。

Heap
PSYoungGen total 149056K, used 125317K [0xa9700000, 0xb41a0000, 0xb41a0000)
eden space 123520K, 95% used [0xa9700000,0xb0ac0de0,0xb0fa0000)
from space 25536K, 26% used [0xb28b0000,0xb2f50748,0xb41a0000)
to space 25600K, 0% used [0xb0fa0000,0xb0fa0000,0xb28a0000)
PSOldGen total 261248K, used 239964K [0x941a0000, 0xa40c0000, 0xa9700000)
object space 261248K, 91% used [0x941a0000,0xa2bf7018,0xa40c0000)
PSPermGen total 163328K, used 130819K [0x841a0000, 0x8e120000, 0x941a0000)
object space 163328K, 80% used [0x841a0000,0x8c160c40,0x8e120000)
堆信息。新生代、老生代、永久代。对JVM有了解的人应该都清楚,不解释了。

Code Cache [0xb4262000, 0xb5ac2000, 0xb7262000)
total_blobs=5795 nmethods=5534 adapters=209 free_code_cache=25103616 largest_free_block=38336
代码缓存(Code Cache)。这是一块用于编译和保存本地代码的内存,注意是本地代码,它和PermGen(永久代)是不一样的,永久带是用来存放Java类定义的。

Dynamic libraries:
00101000-00122000 r-xp 00000000 08:01 3483560 /usr/lib/libjpeg.so.62.0.0
00122000-00123000 rwxp 00020000 08:01 3483560 /usr/lib/libjpeg.so.62.0.0
00125000-00130000 r-xp 00000000 08:01 9093202 /lib/libgcc_s-4.1.2-20080825.so.1
00130000-00131000 rwxp 0000a000 08:01 9093202 /lib/libgcc_s-4.1.2-20080825.so.1
... ...
内存映射。这些信息是虚拟机崩溃时的虚拟内存列表区域。在定位崩溃原因的时候,它可以告诉你哪些类库正在被使用,位置在哪里,还有堆栈和守护页信息。就以列表中第一条为例说明:

00101000-00122000:内存区域
r-xp:权限,r/w/x/p/s分别表示读/写/执行/私有/共享
00000000:文件内的偏移量
08:01:文件位置的majorID和minorID
3483560:索引节点号
/usr/lib/libjpeg.so.62.0.0:文件位置
每一个lib都有两块虚拟内存区域——代码和数据,它们的权限不同,代码区域是r-xp;数据区域是rwxp。守护页(guard page)由权限为--xp和rwxp的一对组成。

VM Arguments:
jvm_args: -Dosgi.requiredJavaVersion=1.5 -XX:MaxPermSize=256m -Xms40m -Xmx512m -Dorg.eclipse.swt.browser.XULRunnerPath=''
java_command: /.../eclipse/plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar -os linux -ws gtk -arch x86 -showsplash -launcher /.../eclipse/eclipse -name Eclipse ...
Launcher Type: SUN_STANDARD

Environment Variables:
PATH=...
DISPLAY=:0.0
虚拟机参数和环境变量。

Signal Handlers:
SIGSEGV: [libjvm.so+0x726440], sa_mask[0]=0x7ffbfeff, sa_flags=0×10000004
SIGBUS: [libjvm.so+0x726440], sa_mask[0]=0x7ffbfeff, sa_flags=0×10000004
… …
信号句柄。

OS:Red Hat Enterprise Linux Client release 5.4 (Tikanga)

uname:Linux 2.6.18-164.el5 #1 SMP Tue Aug 18 15:51:54 EDT 2009 i686
libc:glibc 2.5 NPTL 2.5
rlimit: STACK 10240k, CORE 0k, NPROC 65536, NOFILE 1024, AS infinity
load average:1.78 1.58 1.54

/proc/meminfo:

CPU:total 4 (4 cores per cpu, 1 threads per core) family 6 model 42 stepping 7, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3

/proc/cpuinfo:

Memory: 4k page, physical 3631860k(155144k free), swap 5124724k(5056452k free)

系统频繁Full gc问题分析及解决办法

一、场景描述

上周开始系统在业务高峰期一直收到Full gc报警,监控显示fgc频繁,下图是监控图,左边红框是优化前效果,右边是优化后,优化后fgc基本为0

监控
二、原因查找

1.查看gc日志,发现old区fgc后大小没有变化,如下图:

gclog
2.去线上dump内存看是什么对象,用memory analyzer分析,Retained Size竟然有2.4G,全是sun.awt.SunToolkit这个对象,其实到这一步已经可以确定是什么问题了,只是自己对系统不是很熟悉,导致定位具体的问题代码花了一些时间

ma

三、原因分析

系统中有一个调用频繁的接口会调用下面这个方法,目的是获取图片的宽高信息,但是Image这个对象用完不会自动释放,需要手动调用 flush()方法;以前没有调用这个方法,就导致一有请求就会有大对象进入old区,在业务高峰期old区一会就被打满,所以一直进行fgc
public static Image getImage(String path) {
ImageIcon icon = new ImageIcon(path);
Image img = icon.getImage();
return img;
}

四、解决办法

其实不管是用Image还是BufferedImage,读取图片的宽高不用把图片全部加载到内存,在图片的宽高信息其实是存储在文件头中的,只 要按不同的格式读取文件的头信息就可以拿到宽高信息
使用ImageReader代码如下

Iterator readers = ImageIO.getImageReadersByFormatName(StringUtil.getFileSuffix(filePath));
ImageReader reader = (ImageReader)readers.next();
iis = ImageIO.createImageInputStream(is);
reader.setInput(iis, true);
return Pair.of(reader.getWidth(0),reader.getHeight(0));

使用PrintGCDateStamps打印GC时间

之前打印gc日志的时候使用是:-XX:+PrintGCTimeStamps,这个选项记录的是jvm启动时间为起点的相对时间,可读性较差,不利于定位问题,使用PrintGCDateStamps记录的是系统时间,更humanreadable

JVM内存分析命令

jinfo:

[bash]查看Java进程的栈空间大小:sudo -u tomcat /home/java/default/bin/jinfo -flag ThreadStackSize 14750
查看是否使用了压缩指针:sudo -u tomcat /home/java/default/bin/jinfo -flag UseCompressedOops 14750
查看系统属性:sudo -u tomcat /home/java/default/bin/jinfo -sysprops 14750 [/bash]

jstack:

[bash]查看一个指定的Java进程中的线程的状态:sudo -u tomcat /home/java/default/bin/jstack 14750 [/bash]

jstat:

[bash]查看gc的信息:sudo -u tomcat /home/java/default/bin/jstat -gcutil 14750[/bash]

jmap&mat

[bash]sudo -u tomcat /home/java/default/bin/jmap -histo:live 14750

堆空间中各个年龄段的空间的使用情况:sudo -u tomcat /home/java/default/bin/jmap -heap 14750
[/bash]

jmap指定的dump文件一定要是tomcat用户可写,比如可以新创建一个文件夹
sudo mkdir /home/memdump
sudo chown tomcat:tomcat /home/memdump
sudo -u tomcat /home/java/default/bin/jmap -dump:live,format=b,file=/home/memdump/memMap.20131125.hprof 14750

Tomcat内存溢出的原因

在生产环境中tomcat内存设置不好很容易出现内存溢出。造成内存原因是不一样的,当然处理方式也不一样。 这里根据平时遇到的情况和相关资料进行一个总结。常见的一般会有下面三种情况:

1.OutOfMemoryError: Java heap space

2.OutOfMemoryError: PermGen space

3.OutOfMemoryError:unable to create new native thread.

tomcat

对于前两种情况,在应用本身没有内存泄露的情况下可以用设置tomcat jvm参数来解决。(-Xms, -Xmx, -XX:PermSize, -XX:MaxPermSize),最后一种可能需要调整操作系统和tomcat jvm参数同时调整才能达到目的。

第一种:是堆溢出。

在JVM中如果98%的时间是用于GC且可用的Heap size 不足2%的时候将抛出此异常信息。没有内存泄露的情况下,调整-Xms,-Xmx参数可以解决。

-Xms:初始堆大小

-Xmx:最大堆大小

但堆的大小受下面三方面影响:

1.相关操作系统的数据模型(32-bt还是64-bit)限制;(32位系统下,一般限制在1.5G~2G;我在2003 server 系统下(物理内存:4G和6G,jdk:1.6)测试 1612M,64为操作系统对内存无限制。)

2.系统的可用虚拟内存限制;

3.系统的可用物理内存限制。

堆的大小可以使用 java -Xmx***M version 命令来测试。支持的话会出现jdk的版本号,不支持会报错。

-Xms, -Xmx一般配置成一样比较好比如set JAVA_OPTS= -Xms1024m -Xmx1024m 阅读详细 »

5个不能不知的JVM参数

1. DisableExplicitGC
我已记不清有多少次用户要求我就应用程序性能问题提供咨询了,其实只要跨代码快速运行 grep,就会发现清单 1 所示的问题 — 原始 java 性能反模式:

清单 1. System.gc();

[java]
System.gc();

[/java]

显式垃圾收集是一个非常糟糕的主意 — 就像将您和一个疯狂的斗牛犬锁在一个电话亭里。尽管调用的语法是依赖实现的,但如果您的 JVM 正在运行一个分代的垃圾回收器(大多数是)System.gc(); 强迫 VM 执行一个堆的 “全部清扫”,虽然有的没有必要。全部清扫比一个常规 GC 操作要昂贵好几个数量级,这只是个简单数学问题。
您可以不把我的话放在心上 — Sun 的工程师为这个特殊的人工错误提供一个 JVM 标志; -XX:+DisableExplicitGC 标志自动将 System.gc() 调用转换成一个空操作,为您提供运行代码的机会,您自己看看 System.gc() 对于整个 JVM 执行有害还是有利。

阅读详细 »