JVM垃圾回收

JVM (java虚拟机)

参考文献:

Java 虚拟机(JVM )- 内存与垃圾回收篇概述哔哩哔哩bilibili

Java开发必须掌握的线上问题排查命令-HollisChuang's Blog

JVM

JVM:java虚拟机,执行JAVA字节码的虚拟计算机。

是否遇到过这些问题?

- 运行着的线上系统突然卡死,系统无法访问,甚至直接OOM
- JVM GC问题
- JVM参数



所有java程序都运行在JVM上

JVM特点

  • 代码一次编译,到处运行(字节码)

  • 自动内存管理

  • 自动垃圾回收功能

  • 一个JAVA程序进程对应一个JVM

  • JVM允许一个应用有多个线程并行的执行

JVM的整体结构

jvm整体架构图文详解等待、的博客-CSDN博客jvm架构






堆 heap

对进程独立,对线程为共享

栈存放对象、指针,而堆空间存放实例

  • 对象、指针 指向堆中存放的实例。

  • 对象出栈时,堆中对应的内容不会回收;而是新的实例进入堆且堆放不下时,发生GC时,才进行回收。

  • GC的本质是:回收已经出栈了的对象或指针的实例。


基于分代收集理论设计,堆空间分为:

逻辑上分为:新生区+养老区+元空间(方法区)

实际上的堆(heap)分为:新生区+养老区

新生区具体细分为:

Eden + Survivor1 或 Survivor2 (Survivor1与Survivor2只有一个存放信息)

Eden + from 或 to


各区域的默认比例与设置参数

默认情况下:

新生区 : 养老区 = 1/3:2/3

Eden :S1 :S2 = 8/10:1/10:1/10


堆空间大小的设置

这里实际上指 新生区 + 养老区

-Xms:用于表示堆区的起始内存 (-X:是jvm的运行参数;ms:是memory start)

-Xmx:用于表示堆区的最大内存 (mx:是memory maximum)

  • 堆空间,默认单位为kb,但后面可以接k、m、G作为单位,如:-Xms 6144k;-Xms 6m;

  • 不设置堆空间大小时,默认初始内存大小为:主机内存/64,最大内存大小为:主机内存/4

  • 开发中建议将-Xms与-Xmx设置为一样,原因为:-Xms与-Xmx不一致时,内存不足时会进行扩容,内存充裕时会进行缩容,造成系统资源的不必要损耗。

  • 一旦堆区中的内存大小超过 '-Xmx',将会抛出OOM(outofmemory)


查看堆使用率

(一)方法一
# 获取pid
jps
jstat -gc pid

(二)方法二
# 添加参数
-XX:+PrintGCtails


实例分配的过程与回收过程

(1)新对象申请
(2)Eden判断是否放得下
 Eden放得下,将新对象分配到Eden
 Eden放不下,发生一次YGC,YGC后再进行判断Eden是否放得下
 Eden放得下,将新对象分配到Eden
 超大对象,Eden放不下,判断Old是否放得下
 Old放得下,将新对象分配到Old
 Old放不下,发生一次FGC,FGC后再进行判断Old是否放得下
 Old放得下,将对象分配到Old
 Old放不下,发生outofmemory(OOM)

YGC的动作:
(1)YGC时,回收Eden与Servivor中没有对象的实例
 剩余的实例存放到空的S0/S1中,这种情况是S0/S1空间足够
 这个时候会判断对象是否age>阈值(默认15),是则直接放到养老区
 S0/S1空间不足时,将实例放到养老区

FGC的动作:
(1)YGC时,回收Old中没有对象的实例



## 特别需要注意的:
(1)发生GC时,会导致用户线程(作业线程)暂停。
(2)实例每在新生区移动一次,age+1


MinorGC、MajorGC、FullGC的区别

GC按照回收区域分为两大种类型:

(1)部分收集(Partial GC)

(2)整堆收集(Full GC)

  • MinorGC == Young GC,新生区收集,只针对Eden、S0、S1

  • MajorGC == Old GC,养生区收集,只针对Old

  • Full GC,整堆收集,收集整个java堆和方法区的垃圾收集


新生区GC触发机制与特性

  • 触发机制:Eden空间不足,触发YGC

  • Minor GC触发很频繁,回收速度也较快

  • GC会引发STW,暂停用户线程(作业线程),等垃圾回收结束,用户线程才恢复运行


养生区触发机制与特性

特别的:目前,只有CMS收集器会有单独收集老年代的行为。其他收集器均无此行为。

  • 触发机制:Old空间不足,触发MajorGC(Old GC)




Full GC触发机制与特性

对整堆(新生代,养生代)和方法区的垃圾收集。

触发机制:

  • 调用system.gc(),系统建议执行Full GC,但是不一定会执行;

  • 养生代空间不足;

  • 方法区空间不足;

  • 通过Minor GC后进入养生代的实例大于养生代的可用内存

  • S0/S1复制时,Survivor区空间不足,将实例转存到养生代,但实例大于养生代的可用内存

总结

  • Minor GC是清理新生代,但Survivor区满时不会触发;

  • Major GC是清理养生代;

  • Full GC是清理整个堆和方法区,包括新生代,养生代,方法区。






JAVA堆分代思想

  • 分代的唯一理由就是优化GC性能。

  • 若不进行分代,则每次GC都需要堆所有区域进行扫描。


内存分配策略

  • 优先分配到Eden

  • 大对象直接分配到养生区

  • 长期存活的对象分配到养生区




TLAB (给每个线程单独分配的缓冲区,私有)

thread local allocation buffer

优势:提高对象的创建效率


为什么有TLAB

  • 堆区是线程共享区域,任何线程都可以访问到堆区中的共享数据

  • 由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的

  • 为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度。

  • 所以进入TLAB机制!!!

什么是TLAB

  • 对Eden区域继续进行划分,JVM为每个线程分配了一个私有缓存区域

  • 一旦对象在TLAB空间分配内存失败时(实例过大,或TLAB写满等),JVM就会尝试通过使用加锁机制确保数据操作的原子性,从而直接在Eden空间中分配内存

  • TLAB默认开启

  • 默认下TLAB空间非常小,仅占整个Eden空间的1%

参数:

# TLAB的开与关
-XX:+UseTLAB
-XX:-UseTLAB

# 设置TLAB空间所占用Eden空间的百分比大小
-XX:TLABWasteTargetPercent=1


堆空间的参数设置

-Xms10m
设置堆最小值为10M

-Xmx10m
设置堆最大值为10M

-XX:SurvivorRatio=8
年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:1,一个Survivor区占整个年轻代的1/10

-XX:NewRatio=3
设置年轻代(EC+S0C+S1C)和年老代(OC)的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4(jdk1.8,默认2)

-XX:+PrintGCDetails
打印GC的具体信息


-XX:loggc:D:/a.log
将jvm的日志存储到指定文件

#参考链接
https://www.jianshu.com/p/bcdae3c80c1d

你可能感兴趣的:(JVM垃圾回收)