JVM内存管理和垃圾收集
来源:http://liuxuan.info/blog/2011/04/03/jvm-memory-management-and-gc/
一.Java基本类型和类的大小
1.Java手册上的类型大小
byte:8-bit
short:16-bit
Int:32-bit
long:64-bit
char:16bitunsignedinteger
float:32-bit
double:64-bit
boolean:1-bit
2.实际存储大小(根据JVM实现而定)
Byte:16bytes
Short:16bytes
Integer:16bytes
Long:16bytes
Character:16bytes
Float:16bytes
Double:16bytes
Boolean:16bytes
Object:8bytes
二.JVM的堆结构
运行时数据区域,所有类实例和数组的内存均从此处分配,由Java虚拟机启动时创建。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。
堆由两部分组成:
Eden+FromSpace+ToSpace也叫做YoungGeneration(年轻代),TenuredSpace也叫做OldGeneration(老年代)。
SurvivorSpace包括S0和S1。
PermanentSpace(方法区):JVM具有一个由所有线程共享的方法区。它存储每个类结构,如运行时常数池、字段和方法数据,以及方法和构造方法的代码,它是在Java虚拟机启动时创建的,不包括在JVM堆内,默认为4M。
这个区域主要存放:
1.Class的信息
包名
父类的包名
类或接口
类型修饰符
父接口包名
2.其它信息
类型的常量池
属性
方法
类里的静态变量(除了常量)
一个指向ClassLoader的引用
一个指向Class类的引用
三.GC的工作流程
绝大多数情况下对象初始化被分配在Eden中(一个非常大的对象会被分配在老年代中)。
如果Eden空间占满了,会触发minorGC。MinorGC后仍然存活的对象会被复制到S0中去。这样Eden就被清空可以分配给新的对象。
又触发了一次MinorGC,S0和Eden中存活的对象被复制到S1中,并且S0和Eden被清空。在同一时刻,只有Eden和一个SurvivorSpace同时被操作。
当每次对象从Eden复制到SurvivorSpace或者从SurvivorSpace中的一个复制到另外一个,有一个计数器会自动增加值。默认情况下如果复制发生超过16次,JVM会停止复制并把他们移到老年代中去。
如果一个对象不能在Eden中被创建,它会直接被创建在老年代中。如果老年代的空间被占满会触发老年代的GC,也被称为FullGC。fullGC是一个压缩处理过程,所以它比MinorGC要慢很多。
四.GC算法
SerialCollector
大部分平台或者强制java-client默认会使用这种。YoungGeneration=SerialOldGeneration=Serial(Mark-Sweep-Compact)这种方法的缺点很明显,Stop-the-world,速度慢。服务器应用不推荐使用。
ParallelCollector
在Linuxx64上默认是这种算法,其他平台要加java-server参数。YoungGeneration=parallel,多个thread同时copyOldGeneration=Mark-Sweep-Compact=1优点:新生代回收更快。因为系统大部分时间做的GC都是新生代的,这样提高了Throughput(CPU用于非GC时间)。缺点:当运行在8G/16GServer上OldGeneration存活对象太多的时候暂停时间(PauseTime)过长。
ParallelCompactingCollector(ParallelOld)
YoungGeneration=parallelOldGeneration=parallel优点:OldGeneration上性能较ParallelCollector方式有提高。缺点:大部分Server系统OldGeneration内存占用会达到60%-80%,Compact方面开销比起ParallelCollector并没明显减少。
ConcurentMark-Sweep(CMS)Collector(low-latencycollector)
YoungGeneration=parallelOldGeneration=CMS同时不做compact操作。优点:PauseTime会降低,PauseTime敏感但CPU有空闲的场景需要建议使用策略4。缺点:CPU占用过多,CPU密集型服务器不适合。需要更大的堆空间,会造成很多碎片。
五.JVM的默认设置
1.堆(heap)即(NewGeneration和OldGeneraion之和)的设置
初始分配的内存由-Xms指定,默认是物理内存的1/64但小于1G。
最大分配的内存由-Xmx指定,默认是物理内存的1/4但小于1G。
默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio指定。
默认空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio指定。
服务器一般设置-Xms、-Xmx相等以避免在每次GC后调整堆的大小,所以上面的两个参数没啥用。
-Xmn设置YoungGeneration的heap大小
-XX:MinHeapFreeRatio与-XX:MaxHeapFreeRatio设定空闲内存占总内存的比例范围,这两个参数会影响GC的频率和单次GC的耗时。-XX:NewRatio决定Young与OldGeneration的比例。Younggeneration空间越大,MinorGC频率越低,但是OldGeneration空间小了,又可能导致MajorGC频率增加。-XX:NewSize和-XX:MaxNewSize直接指定了YoungGeneration的缺省大小和最大大小。
2.非堆内存的设置
默认分配为64M
-XX:PermSize设置最小分配空间,-XX:MaxPermSize设置最大分配空间。一般把这两个数值设为相同,以减少申请内存空间的时间。
六.GC调优
参考:GC调优例子
设置Xms=Xmx=3/4物理内存,-Xmn为1/4的-Xmx值
如果是CPU密集型服务器,使用–XX:+UseParallelOldGC,否则–XX:+UseConcMarkSweepGC
新生代,Parallel/ParallelOld可设大于Xmx1/4,CMS可设小,小于Xmx1/4
经验之谈:通常情况下,JVM堆的大小应为物理内存的80%
七.Dumpheap
1.Linux下:jmap-dump:file=xxx.hprofpid,其中pid通过ps-aux命令查看
2.命令行:jstat-gcutilpidp1p2,其中p1表示每多少毫秒打印一次;p2表示一共打印几次。
输出的参数含义:
S0:Heap上的Survivorspace0段已使用空间的百分比
S1:Heap上的Survivorspace1段已使用空间的百分比
E:Heap上的Edenspace段已使用空间的百分比
O:Heap上的Oldspace段已使用空间的百分比
P:Permspace已使用空间的百分比
YGC:从程序启动到采样时发生YoungGC的次数
YGCT:YoungGC所用的时间(单位秒)
FGC:从程序启动到采样时发生FullGC的次数
FGCT:FullGC所用的时间(单位秒)
GCT:用于垃圾回收的总时间(单位秒)
jstat命令其他参数含义:
jstat-classpid:显示加载class的数量,及所占空间等信息。
jstat-compilerpid:显示VM实时编译的数量等信息。
jstat-gcpid:可以显示gc的信息,查看gc的次数,及时间。其中最后五项,分别是younggc的次数,younggc的时间,fullgc的次数,fullgc的时间,gc的总时间。
jstat-gccapacity:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推,OC是old内纯的占用量。
jstat-gcnewpid:new对象的信息。
jstat-gcnewcapacitypid:new对象的信息及其占用量。
jstat-gcoldpid:old对象的信息。
jstat-gcoldcapacitypid:old对象的信息及其占用量。
jstat-gcpermcapacitypid:perm对象的信息及其占用量。
jstat-utilpid:统计gc信息统计。
jstat-printcompilationpid:当前VM执行的信息。
3.GCLog
-Xloggc:d:\gc.log-XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCTimeStamps–addtimestamp
八.OutofMemory-java.lang.OutOfMemoryError
1.Javaheapspace
Configurationissue:-Xmx
MemoryLeak:Theexcessiveuseoffinalizers
PermGenspace:Toomanyclasses-XX:MaxPermSize
RequestedarraysizeexceedsVMlimit:Needasobigarray?
Requestbytesfor:Outofswapspace?
NativeMemoryLeak
(Nativemethod)
NativeMemoryAllocationIssue
2.PermMemoryLeak
(1)TooManyInternedString
String.intern()
ConstantStringwillbeinternedimplicitly
NoEnoughInfoprovidedbyHeapDumponInternedString
IfPermMemoryincreaseddynamically,becareful
(2)TooManyClassesorClassLoadLeak
Enlargethepermgeneration
Avoidduplicatedclassloader
3.OutofSwapSpace
Enlargetheswapspace
Systemswith4GBoframorlessrequireaminimumof2GBofswapspace
Systemswith4GBto16GBoframrequireaminimumof4GBofswapspace
ForUnixFamilyOS,usepmdumporpmap,libumemforSolaris