JVM 垃圾回收机制

这两天一直在跟进一个内存的问题,由于之前对JVM的内存理解的过于粗浅,很多概念都不甚清楚,经常看到YC和OC,根本不知道是什么意思;

借这个机会刚好补了一下知识,希望对大家有用

先看一下JVM运行后有哪些和内存相关的东西:

java堆:当Java程序创建一个类的实例或者数组时,都在堆中为新的对象分配内存。虚拟机中只有一个堆,所有的线程都共享他;可以理解为:新建的对象都是在这个区域申请内存的

方法区:主要存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息;就是后面我们会介绍到的永久代,在应用启动完后大小就基本固定了,后续基本不会增长;因为对象的内存申请都是在java堆里面

java栈区:就是具体的方法调用,平时我们看到的threaddump就是在这里;包含java的栈和本地方法栈

JavaStack(java的栈):虚拟机只会直接对Javastack执行两种操作:以帧为单位的压栈或出栈 Nativemethodstack(本地方法栈):保存native方法进入区域的地址

jvm运行时的数据区

由于大部分内存都是集中在java堆中,主要介绍java堆的相关配置和gc的过程;初次接触,理解的可能还有点肤浅,大家轻点喷

主要参考链接:http://blog.csdn.net/zhb123gggggg/article/details/40901003

(1) java对象申请的内存并不会在程序内部主动释放,当然你也可以主动调用system.gc(),但是程序基本都没这么做过

java的内存管理都会交给jdk去处理,jdk有一套自己的内存管理和回收机制;当jvm内存占用达到一定的值,就会触发内存回收;也就是我们常看到的YC和OC

(2) 那么什么是YC(Minor GC)、什么是OC(Full GC)呢? YC是young collection的缩写,也就是新生代回收,OC是old collection的回收,也就是老年代回收

注:jdk叫Minor GC,jrokit叫YC

触发YC是非常正常的事情,因为随着new对象的不断增加,内存也会不断上升,当达到新生代内存的最大值时,就会触发YC操作释放新生代的内存,但前提是YC的时间控制在ms范围内; 而OC是对整个堆进行垃圾回收,回收的时间长,所以我们需要尽量减少OC的发生频率

(3) YC、OC的相关参数是那些呢?

先了解几个参数的含义:

-Xms和-Xmx是不是很熟悉?对的,这就是配置在我们env文件里最常用的两个参数,一般配置成一样,原因是将heap初始可用内存尽量调大,避免内存不够用发生gc后再去调整heap的大小

-Xmn也是配置在env里的常用参数,就是前面提到的新生代内存的大小;为什么对于新生代和老年代的配置只需要明确指出这一项呢,应为其他参数都是有默认比例的,也就是Ratio和SurvivorRatio

为什么我们看到的instance的内存占用最大的时候会超过-Xmx的值呢,那是因为还有永久代的内存:XX:PermSize 和 -XX:MaxPermSize;永久代主要存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息

注:jrokit里是通过-Xns来设置新生代内存大小的(也就是nursery区)

  jrokit是没有永久代的,并且jdk1.8版本也移除了永久代,其目的是Hotspot JVM和JRockit JVM相融合的设计思路。
  转移位置: 将java类部分放到java heap里,将字符串常量和类中的静态变量放到内存里面。

-Xms 初始堆大小。如:-Xms256m -Xmx 最大堆大小。如:-Xmx512m -Xmn 新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90% -Xss JDK1.5+ 每个线程堆栈大小为 1M,一般来说如果栈不是很深的话, 1M 是绝对够用了的。 -XX:NewRatio 新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3;这也是新生代一般配置为1/4的原因 -XX:SurvivorRatio 新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10 -XX:PermSize 永久代(方法区)的初始大小 -XX:MaxPermSize 永久代(方法区)的最大值 -XX:+PrintGCDetails 打印 GC 信息 -XX:+HeapDumpOnOutOfMemoryError让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分析

(4) YC、OC的过程又是怎样的?

下图描述了新生代和老年代的比例以及内存回收的过程;   

绝大多数刚创建的对象会被分配在Eden区,其中的大多数对象很快就会消亡。Eden区是连续的内存空间,因此在其上分配内存极快; 当Eden区满的时候,执行Minor GC,将消亡的对象清理掉,并将剩余的对象复制到一个存活区Survivor0(此时,Survivor1是空白的,两个Survivor总有一个是空白的); 此后,每次Eden区满了,就执行一次Minor GC,并将剩余的对象都添加到Survivor0; 当Survivor0也满的时候,将其中仍然活着的对象直接复制到Survivor1,以后Eden区执行Minor GC后,就将剩余的对象添加Survivor1(此时,Survivor0是空白的)。 当两个存活区切换了几次(HotSpot虚拟机默认15次,用-XX:MaxTenuringThreshold控制,大于该值进入老年代)之后,仍然存活的对象(其实只有一小部分,比如,我们自己定义的对象),将被复制到老年代。 从上面的过程可以看出,Eden区是连续的空间,且Survivor总有一个为空。经过一次GC和复制,一个Survivor中保存着当前还活 着的对象,而Eden区和另一个Survivor区的内容都不再需要了,可以直接清空,到下一次GC时,两个Survivor的角色再互换。因此,这种方 式分配内存和清理内存的效率都极高,这种垃圾回收的方式就是著名的“停止-复制(Stop-and-copy)”清理法(将Eden区和一个Survivor中仍然存活的对象拷贝到另一个Survivor中),这不代表着停止复制清理法很高效,其实,它也只在这种情况下高效,如果在老年代采用停止复制,则挺悲剧的。

在Eden区,HotSpot虚拟机使用了两种技术来加快内存分配。分别是bump-the-pointer和TLAB(Thread- Local Allocation Buffers),这两种技术的做法分别是:由于Eden区是连续的,因此bump-the-pointer技术的核心就是跟踪最后创建的一个对象,在对 象创建时,只需要检查最后一个对象后面是否有足够的内存即可,从而大大加快内存分配速度;而对于TLAB技术是对于多线程而言的,将Eden区分为若干 段,每个线程使用独立的一段,避免相互影响。TLAB结合bump-the-pointer技术,将保证每个线程都使用Eden区的一段,并快速的分配内 存。

年老代(Old Generation):对象如果在年轻代存活了足够长的时间而没有被清理掉(即在几次 Young GC后存活了下来),则会被复制到年老代,年老代的空间一般比年轻代大,能存放更多的对象,在年老代上发生的GC次数也比年轻代少。当年老代内存不足时, 将执行Major GC,也叫 Full GC。  

可以使用-XX:+UseAdaptiveSizePolicy开关来控制是否采用动态控制策略,如果动态控制,则动态调整Java堆中各个区域的大小以及进入老年代的年龄。 如果对象比较大(比如长字符串或大数组),Young空间不足,则大对象会直接分配到老年代上(大对象可能触发提前GC,应少用,更应避免使用短命的大对象)。用-XX:PretenureSizeThreshold来控制直接升入老年代的对象大小,大于这个值的对象会直接分配在老年代上。

可能存在年老代对象引用新生代对象的情况,如果需要执行Young GC,则可能需要查询整个老年代以确定是否可以清理回收,这显然是低效的。解决的方法是,年老代中维护一个512 byte的块——”card table“,所有老年代对象引用新生代对象的记录都记录在这里。Young GC时,只要查这里即可,不用再去查全部老年代,因此性能大大提高。

YC、OC

(5) 几种GC机制:

在执行机制上JVM提供了串行GC(SerialGC)、并行回收GC(ParallelScavenge)和并行GC(ParNew) 1)串行GC 在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求 不是非常高的应用上,是client级别默认的GC方式,可以通过-XX:+UseSerialGC来强制指定 2)并行回收GC 在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是 server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数 3)并行GC 与旧生代的并发GC配合使用 旧生代的GC: 旧生代与新生代不同,对象存活的时间比较长,比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减少内存碎片带来的效率损耗。在执行机制上JVM提供了串行 GC(SerialMSC)、并行GC(parallelMSC)和并发GC(CMS),具体算法细节还有待进一步深入研究

(6) jrokit查看内存的命令:

jstat命令可以参考这个链接:http://blog.csdn.net/chenyi8888/article/details/8484626

jstat -options:查看jstat可用的参数

jrokit查看内存的命令

jstat -gc 517588 1000 5 :表示进程517588每1秒刷新一次,呈现5次的gc情况

输入图片说明

如果想查看对象的内存分配情况,可以使用jrcmd命令:

jrcmd用法可以用jrcm help看一下哈,还有很多有用的参数(例如打threaddump也是通过jrcmd)

...../jrockit-jdk1.6.0_37-R28.2.5-4.1.0/bin/jrcmd 64943 print_object_summary |more
64943:
--------- Detailed Heap Statistics: ---------
37.6% 115666k   987021 +115666k java/net/SocksSocketImpl
12.5% 38555k   987017 +38555k java/net/SocketInputStream
10.0% 30889k  3953877 +30889k java/lang/Object
 7.5% 23134k   987051 +23134k java/net/Inet4Address
 7.5% 23133k   987018 +23133k java/net/Socket
 5.0% 15436k   987937 +15436k java/util/concurrent/atomic/AtomicInteger
 5.0% 15426k   987296 +15426k java/io/FileDescriptor
 4.8% 14836k   147270 +14836k [C
 1.2% 3813k   162703  +3813k java/lang/String
 0.7% 2272k    20718  +2272k [Ljava/lang/Object;
 0.7% 2240k    20483  +2240k java/lang/Class
 0.6% 1890k    25790  +1890k [Ljava/util/HashMap$Entry;
     307931kB total ---

--------- End of Detailed Heap Statistics ---

(6) jdk查看内存的命令:

jmap的使用方法可以参考:http://www.cnblogs.com/xingzc/p/5778014.html

查看当前堆和永久代的使用情况:

....../jdk1.7.0_67/bin/jmap -heap 495149
Attaching to process ID 495149, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.65-b04

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 1073741824 (1024.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 134217728 (128.0MB)
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 268435456 (256.0MB)
   G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 120848384 (115.25MB)
   used     = 46900504 (44.727806091308594MB)
   free     = 73947880 (70.5221939086914MB)
   38.80937621805518% used
Eden Space:
   capacity = 107479040 (102.5MB)
   used     = 45424552 (43.320228576660156MB)
   free     = 62054488 (59.179771423339844MB)
   42.263637635766% used
From Space:
   capacity = 13369344 (12.75MB)
   used     = 1475952 (1.4075775146484375MB)
   free     = 11893392 (11.342422485351562MB)
   11.039823644301471% used
To Space:
   capacity = 13369344 (12.75MB)
   used     = 0 (0.0MB)
   free     = 13369344 (12.75MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 939524096 (896.0MB)
   used     = 263382056 (251.18070220947266MB)
   free     = 676142040 (644.8192977905273MB)
   28.033560514450073% used
Perm Generation:
   capacity = 46530560 (44.375MB)
   used     = 46330736 (44.18443298339844MB)
   free     = 199824 (0.1905670166015625MB)
   99.57055320202464% used

查看永久代里的类分布情况:

....../jdk1.7.0_67/bin/jmap -permstat 495149
Attaching to process ID 495149, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.65-b04
finding class loader instances ..done.
computing per loader stat ..done.
please wait.. computing liveness.............liveness analysis may be inaccurate ...
class_loader	classes	bytes	parent_loader	alive?	type

	2656	15721528	  null  	live	
0x00000000c7886180	1	1888	0x00000000bbc42350	dead	sun/reflect/DelegatingClassLoader@0x00000000f004fcd8
0x00000000b805c970	995	7300152	0x00000000b805c9c0	live	sun/misc/Launcher$AppClassLoader@0x00000000f0229560
0x00000000b8089080	1	3064	0x00000000b805c970	dead	sun/reflect/DelegatingClassLoader@0x00000000f004fcd8
0x00000000c7b48b00	1	3064	0x00000000c66467f0	dead	sun/reflect/DelegatingClassLoader@0x00000000f004fcd8
0x00000000c7bbde00	1	1888	0x00000000c66467f0	dead	sun/reflect/DelegatingClassLoader@0x00000000f004fcd8
0x00000000b8060c90	1	3064	0x00000000b805c970	dead	sun/reflect/DelegatingClassLoader@0x00000000f004fcd8
0x00000000c74b6178	1	3064	0x00000000c66467f0	dead	sun/reflect/DelegatingClassLoader@0x00000000f004fcd8
0x00000000c65d93f8	1	3032	  null  	dead	sun/reflect/DelegatingClassLoader@0x00000000f004fcd8
0x00000000c7b08788	1	1888	0x00000000c66467f0	dead	sun/reflect/DelegatingClassLoader@0x00000000f004fcd8
0x00000000c799cf20	1	3064	0x00000000c66467f0	dead	sun/reflect/DelegatingClassLoader@0x00000000f004fcd8
0x00000000c7b0dae8	1	3032	0x00000000c66467f0	dead	sun/reflect/DelegatingClassLoader@0x00000000f004fcd8

查看堆里的对象分布情况:

....../jdk1.7.0_67/bin/jmap -histo 495149|more

 num     #instances         #bytes  class name
----------------------------------------------
   1:        123387       15578696  [C
   2:         24053       14138632  [B
   3:         84482       12337176  
   4:         84482       10825712  
   5:          7539        8760408  
   6:          7539        5437192  
   7:          6354        5003168  
   8:        121157        2907768  java.lang.String
   9:          2979        1698368  
  10:         19539        1563120  java.lang.reflect.Method
  11:         36684        1467360  java.util.LinkedHashMap$Entry
  12:          4797        1143976  [Ljava.util.HashMap$Entry;
  13:          4611        1126240  [I
  14:         34154        1092928  java.util.HashMap$Entry
  15:          8145         986832  java.lang.Class
  16:         12069         733352  [Ljava.lang.Object;
  17:         10887         669408  [S
  18:         12404         652928  [[I

转载于:https://my.oschina.net/saulc/blog/828424

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值