回主页

【JVM】HotSpot JVM内存管理和GC策略总结


JVM的相关知识是学习java高级特性必须要去深入学习的。平时也有一些学习和实践,不过总结比较少。 今天有时间总结一下最基础的内存模型和GC策略的知识,在此记录一下。

hotspot jvm内存模型

1.内存模型

hotspot的内存模型很多地方都有类似总结,我也简单总结了一下,大概可以用下图表示:

关于几个分区的描述定义:

jvm 内存模型

jvm 内存模型

  1. 线程栈:线程创建是会为每个线程创建一个线程栈,线程栈里面会为每个方法调用创建一个栈帧。主要用于保存线程的当前运行状态。

  2. 堆:用于存放运行时中生成的新对像。会划分成新生代和老年代。新生代里面又划分成了eden区、存活1区和存活2区。

  3. 永久区:方法和常量区,用于存放方法字节码元数据和各种常量。

为什么堆会划分为新生代和老年代?

基本原理:对于大部分应用,常驻对象不多。因为大部分存活寿命不长,新生代和老年代的划分有利于区分对待和缩小垃圾回收范围。(Most allocated objects are not referenced (considered live) for long, that is, they die young. Few references from older to younger objects exist.)

2.内存相关启动参数

内存相关常见jvm参数
参数 含义
-Xms 最小堆空间
-Xmx 最大堆空间
-Xss 新生代空间
-XX:PermSize=xxx 永久代空间
-XX:MaxPermSize=xxx 最大永久代空间
-XX:SurviorRatio=xxx 代表eden:s0的比例
-XX:NewRatio=xx 新生代和旧生代的比例.
-XX:MaxTenuringThreshold 在新生代最大存活次数

hotspot 内存垃圾回收策略总结

1.内存回收策略和常见概念

常见内存回收策略可以从以下几个维度来理解:

1.1 串行&并行

串行:单线程执行内存回收工作。十分简单,无需考虑同步等问题,但耗时较长,不适合多cpu。

并行:多线程并发进行回收工作。适合多CPU,效率高。

1.2 并发& stop the world

stop the world:jvm里的应用线程会挂起,只有垃圾回收线程在工作进行垃圾清理工作。简单,无需考虑回收不干净等问题。

并发:在垃圾回收的同时,应用也在跑。保证应用的响应时间。会存在回收不干净需要二次回收的情况。

1.3 压缩&非压缩&copy

压缩:在进行垃圾回收后,会通过滑动,把存活对象滑动到连续的空间里,清理碎片,保证剩余的空间是连续的。 非压缩:保留碎片,不进行压缩。

copy:将存活对象移到新空间,老空间全部释放。(需要较大的内存。)

一个垃圾回收算法,可以从上面几个维度来考虑和设计,而最终产生拥有不同特性适合不同场景的垃圾回收器。

2.HotSpot JVM的YGC&FGC

YGC :对新生代堆进行GC。频率比较高,因为大部分对象的存活寿命较短,在新生代里被回收。性能耗费较小。

FGC :全堆范围的GC。默认堆空间使用到达80%(可调整)的时候会触发FGC。以我们生产环境为例,一般比较少会触发FGC,有时10天或一周左右会有一次。

3.常见GC算法和jvm参数

3.1.串行垃圾收集器

新生代和老生代因为结构划分不一样,其串行收集器算法也不一样

新生代串行收集器采用stop the world策略,步骤大概是:先从eden区扫描,把存活的对象拷贝到to区,如果to区放不下的对象直接拷贝到old区。再从from区扫描存活对象,如果对象存活次数超过阀值的就移到老年区,其他的移到to区。做完之后from和to区概念互换(from和to只是运行时的概念,其实就对应存活1区和存活2区)。

图形的表示如下:

回收前:

回收前

回收前

回收后:

回收后

回收后

老生代串行收集器,老生代垃圾回收主要分为三个阶段 Mark-sweep-compact

Mark :识别哪些是存活的

Sweep : 识别垃圾,并回收

Compact :滑动活动对象并压缩到连续空间,碎片整理。

串行垃圾回收器在jvm client模式下是默认启动的。参数 -XX:+UseSerialGC 可以设置垃圾回收策略为串行。

3.2并行垃圾回收器

主要以下特点:

充分利用CPU

吞吐量优先

和串行一样,不过是多线程执行,缩短了stop-the-world时间

-server模式下默认的回收器。参数 -XX:+UseParallelGC 可以设置垃圾回收策略为并行。

3.3并行压缩收集器(Parallel Compacting Collector)

只对老生代适用,新生代仍旧和并行垃圾回收器一样。

其过程大概如下:

标记阶段 ,使用多线程对存在引用的对象进行并行标记。

分析阶段 ,GC对各个区域进行分析,GC认为,在经过上次GC后,越左边的区域,有引用的对象密度要远远大于右边的区域。所以就从左往右分析,当某个区域的密度达到一个值的时候,就认为这是一个临界区域,所以这个临界区域左边的区域,将不会进行压缩,而右边的区域,则会进行压缩。

压缩阶段 ,多各个需要压缩的区域进行并行压缩

参数-XX:+UseParallelOldGC 可以设置老生代垃圾回收策略为并行压缩。

3.4 Concurrent Mark-Sweep (CMS) Collector

主要特点:

仍旧是老生代适用。

减少停顿,以响应时间为优先。

只有标记和清除,不会进行会压缩。

初始标记和清除支持和应用程序并发执行,中间还是会有一re-mark需要stop the world。

参数-XX:+UseConcMarkSweepGC 可以设置老生代垃圾回收策略为CMS。

3.5 G1垃圾收集器

是在JDK7里支持的,用于取代CMS。具体具体见:http://docs.oracle.com/javase/7/docs/technotes/guides/vm/G1.html

总结

本文的内容只是仅限于基础层面的的一些知识总结,更加深入的知识点还需要后续深入学习。

以下提供一些学习参考:

memory management whitepaper

JVM option

blue davy的一个jmm分享