写下次博客仅仅是因为,自己闲暇之余,从网上搜集各个点,可能未能全尽人意,如有不当或错误之处,希望可以共同讨论。
目录
- 1、java HotSpot client or server
- 2、jvm总体结构
- 2.1、jvm内存分代策略(java 堆)
- 2.2、回收机制详解:
- 3、jvm垃圾回收算法及收集器
- 4、jvm收集器
1、java HotSpot client or server
JVM有两种运行模式Server与Client。两种模式的区别在于,Client模式启动速度较快,Server模式启动较慢;但是启动进入稳定期长期运行之后Server模式的程序运行速度比Client要快很多。这是因为Server模式启动的JVM采用的是重量级的虚拟机,对程序采用了更多的优化;而Client模式启动的JVM采用的是轻量级的虚拟机所以Server启动慢,但稳定后速度比Client远远要快。
可以进行server和client切换,32位电脑和64位不一样。可以在jvm.cfg中切换,谁在上就是什么模式(64位基本是server)
下图是server模式
2、jvm总体结构
2.1、jvm内存分代策略(java 堆)
堆内存划分为几块,一般分为新生代、老年代和永久代(对HotSpot虚拟机而言),这就是JVM的内存分代策略
新生代(Young):新生成的对象优先存放在新生代中,新生代对象朝生夕死,存活率很低,在新生代中,常规应用进行一次垃圾收集一般可以回收70% ~ 95% 的空间,回收效率很高
老年代(Old): 在新生代中经历了多次(具体看虚拟机配置的阀值)GC后仍然存活下来的对象会进入老年代中。老年代中的对象生命周期较长,存活率比较高,在老年代中进行GC的频率相对而言较低,而且回收的速度也比较慢。
永久代(Permanent)java8已移除,代替的是元空间:永久代存储类信息、常量、静态变量、即时编译器编译后的代码等数据,对这一区域而言,Java虚拟机规范指出可以不进行垃圾收集,一般而言不会进行垃圾回收。
新生代GC(Minor GC):Minor GC指发生在新生代的GC,因为新生代的Java对象大多都是朝生夕死,所以Minor GC非常频繁,一般回收速度也比较快。当Eden空间不足以为对象分配内存时,会触发Minor GC。
老年代GC(Full GC/Major GC):Full GC指发生在老年代的GC,出现了Full GC一般会伴随着至少一次的Minor GC(老年代的对象大部分是Minor GC过程中从新生代进入老年代),比如:分配担保失败。Full GC的速度一般会比Minor GC慢10倍以上。当老年代内存不足或者显式调用System.gc()方法时,会触发Full GC。
2.2、回收机制详解:
1)年轻代(Young Gen):年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个Eden Space(伊甸园)和2个Suvivor Space(幸存者区)(命名为A和B)。当对象在堆创建时,将进入年轻代的Eden Space。垃圾回收器进行垃圾回收时,扫描Eden Space和A Suvivor Space,如果对象仍然存活,则复制到B Suvivor Space,如果B Suvivor Space已经满,则复制到Old Gen。同时,在扫描Suvivor Space时,如果对象已经经过了几次的扫描仍然存活,JVM认为其为一个持久化对象,则将其移到Old Gen。扫描完毕后,JVM将Eden Space和A Suvivor Space清空,然后交换A和B的角色(即下次垃圾回收时会扫描Eden Space和B Suvivor Space。这么做主要是为了减少内存碎片的产生。下面是个有趣例子:
HotSpot虚拟机GC算法采用分代收集算法:
1、一个人(对象)出来(new 出来)后会在Eden Space中,当进行GC时,GC会查看每个对象的情况,此对象是否被引用(判断是否生存必要),然后有必要的就会进入Survivor Space(幸存者区),无效的就直接清理掉。
2、当对象进入Survivor Space(幸存者区)后至少可以活一段时间。GC会定期(可以自定义)会对对象进行检查清理,在新生代中经历了多次(具体看虚拟机配置的阀值)GC后仍然存活下来的对象会进入老年代中,生命周期更长,但并不代表就可以永久生存。Full GC依然会清理。
3、jvm垃圾回收算法及收集器
(主要是3个算法,.复制算法和标记-压缩算法(或称为标记-整理算法)、标记-清除算法)
引用计数器算法(也可以算gc算法)
比较古老,也不怎么用·。具体是对象中添加一个引用计数器,如果此对象有一个引用,就增加一个计数。删除一个引用就减少一个计数。然后垃圾回收时,回收计数为0的。无法处理对象引用的,就是对象a指向b,对象b指向a,此时他们引用计数器都不为0,但没有意义了,所以又因引出下面算法,可达性算法
可达性算法这种算法可以有效地避免对象循环引用的情况,整个对象实例以一个树呈现,根节点是一个称为“GC Roots”的对象,从这个对象开始向下搜索并作标记,遍历完这棵树过后,未被标记的对象就会判断“已死”,即为可被回收的对象。
可达性算法
GC算法
3.1、复制算法(Java堆中新生代的垃圾回收算法)
此算法将内存空间划分为2个相等区域,每次只使用其中一个区域,垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另一个区域中。缺点是需要2被内存空间。
Java堆中的新生代就使用了GC复制算法。在新生代中又分为了三个区域:Eden 空间、To Survivor空间、From Survivor空间
3.2、标记-清除算法
标记-清除算法采用从根集合(GC Roots)进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收,如下图所示。标记-清除算法不需要进行对象的移动,只需对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片,而且需要暂停整个应用。
3.3、标记-整理算法
结合“清除”和复制2个算法,相当于优化清除算法。避免内存碎片。标记-压缩算法首先还是“标记”,标记过后,将不用回收的内存对象压缩到内存一端,此时即可直接清除边界处的内存,这样就能避免复制算法带来的效率问题,同时也能避免内存碎片化的问题。老年代的垃圾回收称为“Major GC”。
4、jvm收集器
gc收集器就是对上面gc算法的具体实现。
MinorGC(次收集) 和full GC(全收集)一个是在新生代的回收,一个老年代的回收。所以Minor gc比较频繁,回收速度也快。不会影响老年代。Full gc发生在老年代。至少会伴随一次Minor GC(当年轻代需要回收时会触发Minor GC(也称作Young GC)).FullGC很慢(超过3-5秒就很慢了),而且有Full GC是老年代和新生代都进行回收的。Major GC 虽说是清理老年代,但许多 Major GC 是由 Minor GC 触发的,所以很多情况下将这两种 GC 分离是不太可能的。
触发当内存不足或调用System.gc()方法。
java收集器有7个(下图):(具体我也不熟,暂时就不写了)
上面3个新生代收集器,中间那个是新老通吃,下面3个是老年代收集器。G1应该是最新的。
收集器是新老搭配一起使用的。以下就是连接一起就是指可以老新搭配使用的收集器。
可以多看看CMS收集器
CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器, 如果老年代使用CMS垃圾回收器,需要添加虚拟机参数-“XX:+UseConcMarkSweepGC”。
使用场景:
GC过程短暂停,适合对时延要求较高的服务,用户线程不允许长时间的停顿。
缺点:
服务长时间运行,造成严重的内存碎片化。 另外,算法实现比较复杂(如果也算缺点的话)
为四个步骤:
1. 初始标记 (Stop the World事件 CPU停顿, 很短) 初始标记仅标记一下GC Roots能直接关联到的对象,速度很快;
2. 并发标记 (收集垃圾跟用户线程一起执行) 初始标记和重新标记任然需要“stop the world”,并发标记过程就是进行GC Roots Tracing的过程;
3. 重新标记 (Stop the World事件 CPU停顿,比初始标记稍微长,远比并发标记短)修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但比并发标记时间短;
4. 并发清理 -清除算法;
附:几种GC的区别(注意,以下仅仅是简单,具体需详细讨论):
Minor GC 会清理年轻代的内存
Major GC 是清理老年代。
Full GC 是清理整个堆空间—包括年轻代和老年代。