Java功底 — 掌握JVM这一篇文章就够了

写作这篇文章的目的,是想以浅显易懂的描述让大家重新认识JVM,主要包含下面几个方面

  • JVM架构逻辑
  • JVM堆内存结构模型
  • JVM垃圾回收机制/常用垃圾回收器(Garbage Collector)
  • JVM调优

1.JVM架构逻辑

这张图描绘了JVM和系统调用之间的关系。

  1. 开始是将编译好的字节码文件,经过类加载器,将我们的字节码加载到内存当中,就生成了Class对象。
  2. 绿色表示的方法区和堆区是所有线程共享的内存区域。
  3. 而沙棕色表示的java栈、本地方法栈和程序计数器是运行时线程私有的内存区域,相互独立开。
  • 方法区(Method Area):存放类信息、常量、静态变量
  • 堆(Heap):存放对象实例和数组

Java堆是jvm里内存最大的一块,几乎所有的实例对象都是在这里分配内存,同时堆也是垃圾收集器管理的主要区域,后面有详细描述堆内存的结构。

当方法区和堆区无法满足内存分配需求,也无法再扩展时,就会抛出我们最不想要的OutOfMemoryError异常。

 

2.怎么找到垃圾?

JVM怎么知道堆里的对象是垃圾呢,有两种方式:

  • 引用计数法

每个对象有一个引用计数属性,新增一个引用时计数加1,释放引用时计数减1,计数为0时标记为可以回收。

这个方法虽然简单,但无法解决对象相互引用的问题。

  • 根查找/根可达(Root Searching)

从GC Roots开始向下搜索,搜索走过的路径称为引用链。到一个对象没有引用链相连时,则根不可达,该对象不可用标记为垃圾。

画个简单的示意图,像这样:

顺着线一直能找到的就不是垃圾,线断的(根不可达)的是垃圾。

3.垃圾回收算法

垃圾回收算法,一直以来就三种,不同垃圾回收器只是用的算法不一样。

  • Mark-Sweep(标记清除)
  • Copyting(复制)
  • Mark-Compact(标记整理)

Mark-Sweep(标记清除)

在一块内存中,将要回收的数据,标记直接清除。

坏处:碎片化,清除后内存一个洞一个洞的。

Copyting(复制)

把内存线分为两半,每次只允许用一半。进行垃圾回收的时候,将一半内存中的存活的数据直接复制到另一块内存中,然后清理掉之前的内存区域。

效率是最高的,但会造成内存浪费。

Mark-Compact(标记整理)

将内存中的对象,标记集中移动到内存的一边进行清理,把有用的对象落到前面去(整理)。

效率比Copy略低,好处是把空间让出来了,后面的大对象可以直接在后面分配内存。

4.常用的垃圾回收器有哪些?

算法是GC的策略,而垃圾收集器,就是根据垃圾回收算法来进行垃圾处理的具体实现。

到现在为止,垃圾回收器一共有10种,如下图所示:

生产用的最多的是jdk1.8,默认是Parallel和Parallel Old。

  •  Serial

Serial(单线程)垃圾收集器是最基本、发展历史最悠久的收集器。

垃圾回收时,stop-the-word 停掉所有工作线程(几十毫秒),采用Copy复制算法。

  • Serial Old

Serial Old(单线程)是应用在老年代的垃圾收集器,算法是Mark-Compact 标记整理算法。

  • ParNew

ParNew垃圾收集器是Serial收集器的多线程版本,实现算法和Serial一样是Copy复制算法。

  • Parallel Scavenge

Parallel Scavenge与前面两种新生代的收集器相比,不可以和CMS组合使用,使用算法也是Copy复制算法。它与前两种最大的区别是,他关注的是吞吐量而不是延迟。

  • Parallel Old

Parallel Old是Serial Old的多线程版本,应用在老年代的收集器,实现算法和Serial Old一样是Mark-Compact 标记整理算法。

  • CMS

Concurrent Mark Sweep,并发标记清理。在线程执行过程中也可也进行垃圾收集。分为四个过程:1.初始标记 2.并发标记 3.重新标记 4.并发清理

  • G1(Garbage First)

JDK7就已加入JVM的收集器大家庭中,成为HotSpot重点发展的垃圾回收技术。同优秀的CMS垃圾回收器一样,G1也是关注最小时延的垃圾回收器,也同样适合大尺寸堆内存的垃圾收集,官方也推荐使用G1来代替选择CMS。G1最大的特点是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其他收集器甚至CMS的众多缺陷。

G1采用的标记整理算法。

  • ZGC

zgc是jdk11中要发布的最新垃圾收集器。完全没有分代的概念,先说下它的优点吧,官方给出的是无碎片,时间可控,超大堆。

  • Shenandoah

Shenandoah是一款concurrent及parallel的垃圾收集器,跟ZGC一样也是面向low-pause-time的垃圾收集器,不过ZGC是基于colored pointers来实现,而Shenandoah GC是基于brooks pointers来实现的。

  • Epsilon

Java11新的Epsilon垃圾收集器。

Epsilon(A No-Op Garbage Collector)垃圾回收器控制内存分配,但是不执行任何垃圾回收工作。一旦堆被耗尽,JVM就直接关闭。设计的目的是提供一个完全消极的GC实现,分配有限的内存分配,最大限度降低消费内存占用量和内存吞吐时的延迟时间。

5.GC的演化

其实垃圾回收的演化,是随着现在设备内存大小的增进而演进的。

  • 最初几兆-几十兆

Serial单线程STW垃圾回收。

  • 几十兆-上百兆1G

Parallel 并行多线程进行垃圾回收,但线程不是越多越好,有线程切换耗时。

  • 几G-几十G到上T

Concurrent GC

6.堆内存逻辑分区

在目前1.8中用的最多的,叫分代管理的办法。

画了个堆内存逻辑分区的示意图,如下:

无论内存多大,将内存一分为二。默认新生代和老年代的比例是1:2。新生代比例8:1:1,这个新生代的比例,是根据统计学结果得出的,因为在YGC能回收90%的垃圾。新生代使用算法Copying复制算法,老年代使用的Mark Compact标记整理算法。

到一个对象诞生的时候,先放在新生代,当一个对象被清理了很多次都没被回收就会放到老年代。不同回收收集器都不一样,一般是15或6次。

JVM调优主要就是通过参数调比例。

7.垃圾回收过程

  • 新生代回收 Young GC (YGC)
  • 老年代回收 Old GC (OGC)
  • 当YGC和OGC都发生,就叫Full GC

垃圾回收过程如下:

1.新诞生的对象放在Eden(伊甸园)区

2.当伊甸园区满了,会清理垃圾,把活着的对象复制到survivor(幸存者区),然后把整个Eden清空,这就是一个完整的YGC。

3.如果再来一次,则把Eden活着的和survivor的对象复制到survivor2,同时把Eden和survivor1清空内存,2个survivor中始终保证有一个是空的。

8.JVM调优

JVM调优其实就是通过参数控制内存大小,优化垃圾回收过程。

如下图所示:

最后汇总一下JVM常用参数配置:

类加载设置

  • -XX:+TraceClassLoading      类加载日志
  • -XX:+TraceClassUnloading  类卸载日志

堆设置

 

  • -Xms  初始堆大小
  • -Xmx  最大堆大小
  • -XX:NewSize=n  设置年轻代大小
  • -XX:NewRatio=n  设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
  • -XX:SurvivorRatio=n  年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
  • -XX:MaxPermSize=n  设置持久代大小

收集器设置

 

  • -XX:+UseSerialGC  设置串行收集器
  • -XX:+UseParallelGC  设置并行收集器
  • -XX:+UseParalledlOldGC  设置并行年老代收集
  • -XX:+UseConcMarkSweepGC  设置并发收集器

并行收集器设置

 

  • -XX:ParallelGCThreads=n  设置并行收集器收集时使用的并行收集线程数
  • -XX:MaxGCPauseMillis=n  设置并行收集最大暂停时间
  • -XX:GCTimeRatio=n  设置垃圾回收时间占程序运行时间的百分比,公式为1/(1+n)

并发收集器设置

  • -XX:+CMSIncrementalMode  设置为增量模式,适用於单CPU
  • -XX:ParallelGCThreads=n  设置并发收集器年轻代收集方式为并行收集时,使用的收集线程数

垃圾回收统计信息

  • -XX:+PrintGC
  • -XX:+PrintGCDetails
  • -XX:+PrintGCTimeStamps
  • -Xloggc:filename

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章