Java GC
内存模型与垃圾回收的基本知识
Java的自动内存管理
-
垃圾回收两个派别 -> 手动管理/自动管理
-
自动管理两个派别 -> 引用计数/标记清除
引用计数: 看每个对象被引用的次数,如果是0
的话(没有任何引用),则清除,注意任何被它引用
的对象的计数要减1引用计数的最大问题在于无法处理 循环引用
标记清除: 事先定义好root,然后链子不断延伸
没有被链到的对象都是垃圾(大多数语言所采用,JS/python/Java…) -
内存碎片化问题 任何干净的内存都无法容纳新的大对象,需要做内存压缩
-
内存管理 = GC + 内存压缩
引用类型、GC基本原理
-
引用的类型
- 强引用/strong reference: 命令式语句都是强引用
- 软引用/soft reference: 内存不够的时候会回收它们
- 弱引用/weak reference: 一有GC就回收它们
- 影子引用/phantom reference: 只能拿到影子,拿不到引用本身
- SoftRefernce/WeakReference等 都定义在 java.lang.ref里面
-
垃圾是如何产生的
-
GC的原理
- 从GC roots(确定不是垃圾)出发,做可达性分析,一直到头,剩下的就是垃圾
-
GC roots
- 活的线程/java.lang.Thread
- 类的静态成员
- 线程方法栈所引用的对象
- 说明/线程本身也是GC root
- JNI/java native interface引用的对象
- JNI: 通过接口调用C代码
- 分代GC时其他代的对象
-
GC发生了什么
- stop the world(STW): 所有线程停下
- 清除垃圾
- 压缩内存(老年代)
- copy(新生代)
-
对象的分代假设
- 对象都是用完即弃的,生命周期很短
- 内存的分代
- 年轻代/yong generation
- 老年代/tenured
- 永久代/permanent generation
-
年轻代
-
伊甸园/Eden
无论何时,new一个对象的时候都会i出现在伊甸园中
可分为多个Thread Local Allocation Buffer/TLAB
多线程访问共享内存,要么加锁,要么分为独立的小区域(TLAB选择后者)
TLAB有上限,如果超多则分配common area,要加锁 \ -
Survior(S0/S1)
Yong GC发生时,整个年起代进入其中一个Survior区
比如Eden满了以后,会发生yong GC,所有的非垃圾进入到两个survivor
中的一个,下一次的GC会进入另外一个Survivor,任何时间只有一个survivor
有东西,另一个是空的
足够老的对象被提升到老年代(超过15次GC以后) \
-
-
老年代
- 年龄‘偏大’,发生GC的频率低,清除垃圾,压紧收缩
-
永久代(java 8-)/元空间(java 8+)
- 永久代: 是堆的一部分,存储类数据,字符串常量
- OOM(out of memory)一般原因有两个: 自定义ClassLoader和动态字节码生成
- 元空间: 不是堆的一部分,除非特殊指定,否则没有上限, -XX:MaxMetaspaceSize
-
GC的种类
- Minor GC/Yong GC
- 总是发生在Eden满的时候
- 会发生STW
- Major GC/Full GC
- Major GC清除老年代: 如果对象过大,直接放入老年代
- 这样是不好的,需要把新生代的内存调大一些
- Full GC清除整个堆
- Minor GC/Yong GC
-
分析OOM
- terminal: jps 看到进程号
- jmap -dump:live,format=b,file=`pwd`/process_id.hprof process_id
-
GC算法
- 看图
- JVM option: -XX代表hotspot专有的,+UseSerialGC里面的+代表开启,-代表关闭
-
垃圾回收的过程 -XX:PrintGCDetails -XX:PrintGCDataStamps -XX:PrintGCTimeStamps