今日份学习: Java 的 GC

Java 的 GC

java garbage collection pdf

内存管理

两个流派:

  1. 手动内存管理(C/C++)

  2. 自动内存管理(Java/Python...)

    1. 引用计数(Reference Counting)
    • 问题:循环引用

      • 引用计数的缺点

    1. 标记清除(Mark and Sweep)
    • 可达性分析

  • 线程对应方法栈

  • 方法对应栈帧



内存碎片化

  • 长期工作的内存会出现碎片化
    • 总可用空间仍然足够,但是无法分配大对象
  • 垃圾回收 = 回收可用空间 + 压缩内存



引用的类型

  • 强引用 Strong Reference:用到的都是强引用
  • 软引用 Soft Reference:内存不够的时候会回收他们
  • 弱引用 Weak Reference:一GC就回收他们
  • 影子引用 Phantom Reference:只能拿到影子,拿不到引用本身



GC基本原理

1. 哪些东西是GC Roots?

  • 活的线程(java.lang.Thread)
  • 类的静态成员
  • 线程方法栈所引用的对象
  • JNI(Java Native Interface)引用的对象
  • 分代GC时其他代的对象

2. GC发生了什么?

1.

  • Stop the world (STW)
  • 清除垃圾
  • 压紧内存

2.

  • 拷贝



对象的分代假设

1. 对象的分代假设

  • 内存分代
    • 年轻代(Young Generation)
    • 老年代(Old Generation/Tenured)
    • 永久代(Peramanent Generation)

-XX:SurvivorRatio=n    //Edon区和Survivor区的占比(n/10)
-XX:MaxTenuringThreshold=0    //设置垃圾最大年龄

2. 年轻代

  • Eden (伊甸园)
    • 可以分为多个Thread Local Allocation Buffer


  • Survivor (S0/S1或者from/to)

    • Young GC发生时,整个年轻代进入其中一个Survivor区
  • 足够老的对象被提升到老年代(Tenured)

3. 老年代

  • 老年代相对较大
  • 存储足够年老的对象
  • 发生GC的频率较低
    • 清除垃圾
    • 压紧内存


永久代/元空间

  • Java 8之前:永久代
    • 堆的一部分
    • 存储类数据、字符串常量
    • OOM(OutOfMemory):Permgen space
      • 自定义Classloader
      • 动态字节码生成
    • Java 8+:元空间
      • 不是堆的一部分(-Xmx)
      • 除非特殊指定,否则没有上限
      • -XX:MaxMetaspaceSize/OOM:metaspace



GC的种类

  • Minor GC/Young GC
    • 总是发生在Eden满的时候
    • 会发生STW

- Major GC / Full GC - 这两个概念并没有明确的定义,这里阐述一般理解 - Major GC 清除老年代 - Full GC 清除整个堆


Minor GC 的过早提升:有个对象占用空间非常大,但是年龄又没到 ,却过早提升到老年代 => 需要调整



垃圾回收算法(Java 8)

垃圾回收过程

  • -XX:+PrintGCDetails
  • -XX:+PrintGCDateStamps
  • -XX:+PrintGCTimeStamp,

-XX:+DisableExplicitGC
-XX:+PrintGCDetails
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCApplicationConcurrentTime
-XX:+PrintGCDateStamps
-Xloggc:gclog.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=2000k

>#### 让我们了解每个选项的目的。这些选项可能会因OS / JVM供应商/ JAVA版本而异。
>
>- DisableExplicitGC:默认情况下禁用此选项。有时,开发人员可能通过调用System.gc()或Runtime.getRuntime()。gc()实用地调用了垃圾回收。不建议这样做。因此,在生产系统中,我们启用此选项。这样就禁止了实用的垃圾回收调用。 
>
>- PrintGCDetails:默认情况下禁用此选项。如果启用此选项,则JVM将在每个垃圾收集中打印更多详细信息。
>
>- PrintGCApplicationStoppedTime:默认情况下禁用此选项。如果启用,它将在垃圾回收期间在应用程序暂停时打印信息。
>
>- PrintGCApplicationConcurrentTime:默认情况下,此选项处于禁用状态。如果启用,它将在GC期间打印有关应用程序运行时间的信息。
>
>- PrintGCDateStamps:默认情况下禁用此选项。如果启用,它将在每个GC上打印日期和时间详细信息。
>
>- loggc:这是一个字符串选项。我们必须传递gc日志文件名。在此文件中,我们将获取所有GC日志信息。
>
>- UseGCLogFileRotation:如果文件大小达到指定大小,则此选项指示JVM旋转日志文件。
>
>- NumberOfGCLogFiles:默认值为1。这设置轮换日志时要使用的文件数。
>
>- GCLogFileSize: 默认值为8KB。日志文件的大小,此时将旋转日志。
>
>通过设置以上选项,我们准备获取GC日志。要调整JVM,最好在负载测试期间启用这些选项,并在不同负载上获取GC日志以进行分析。从GC日志中,我们必须观察以下参数。
>
>#### 1. Young GC和Full GC多久发生一次?
>GC事件之间应该有几分钟的时间间隔。如果Young GC发生的频率更高,那么我们可能需要查看分配的Young Gen空间。如果Full GC发生的频率更高,那么我们需要查看分配的堆大小。 
>#### 2. 每个GC事件需要多少时间才能完成?
>理想情况下,Young GC将花费几毫秒,而Full GC将花费几毫秒至几秒。在任何情况下,如果GC需要花费几秒钟到几分钟,那么我们必须查看堆大小的调整。如果GC线程花费更多时间来完成垃圾回收,则应用程序线程将处于等待状态(GC停止了world事件)。这会影响用户体验。

<br><br><br><br><br>
# 题外话
## Heap Dump
Heap dump文件是一个二进制文件,它保存了某一时刻JVM堆中对象使用情况。Heap Dump文件是指定时刻的Java堆栈的快照,是一种镜像文件。Heap Dump一般都包含了一个堆中的Java Objects, Class等基本信息。同时,当你在执行一个转储操作时,往往会触发一次GC,所以你转储得到的文件里包含的信息通常是有效的内容(包含比较少,或没有垃圾对象了) 。我们可以这么理解:heap dump记录内存信息的,thread dump是记录CPU信息的。

## Heap Dump 包含的信息
- 所有的对象信息
对象的类信息、字段信息、原生值(int, long等)及引用值
- 所有的类信息
类加载器、类名、超类及静态字段
- 垃圾回收的根对象
根对象是指那些可以直接被虚拟机触及的对象
- 线程栈及局部变量
包含了转储时刻的线程调用栈信息和栈帧中的局部变量信息
## Heap Dump 获取方式
使用 jmap 命令生成 dump 文件
```jmap -dump:live,format=b,file=c:dumpheap.hprof <pid>```
原文地址:https://www.cnblogs.com/pipemm/p/12489301.html