Java是托管运行对语言,内存对分配和回收是自动进行对。 那么JVM如何确定哪些内存可以被回收呢?这里面是有特定对算法的。
1、GC自动垃圾收集?
Java虚拟机(JVM)垃圾回收GC任务自动确定Java应用程序不再使用的内存并将该内存回收以用于其他用途。
由于内存是在JVM中自动回收的,因此Java开发人员不必考虑复杂的内存释放工作,基本不需要显式释放未使用的内存对象。
类似与自己买菜做饭洗碗和专有保姆买菜做饭洗碗的区别。
工作还是要有人做,只是把复杂琐碎的GC工作交给其他线程来做。
好处就是:省事。解放码农的双手,去干其他的开发工作。
1、GC垃圾回收算法
GC垃圾回收的规则和方法很多,Java经历了20多年的发展,还在不断进化。GC算法也多种多样。这里比较成熟的CMS。还有比较新的算法G1。无论何种算法,都有一个最重要的问题:
如何认定某个对象可以被GC垃圾回收?
GC回收垃圾操作基于以下假设前提:Java中大多数对象都是短命的,并且可以在创建后不久将其回收。
由于未引用的对象会自动从堆内存中删除,因此GC使Java的内存效率更高。
GC自动垃圾回收是查看堆内存,识别正在使用的对象和未使用的对象以及删除未使用的对象的过程。
使用中的对象或引用的对象意味着程序的某些部分仍维护着指向该对象的指针。
程序的任何部分都不再引用未使用的对象或未引用的对象。 因此,GC可以回收未引用对象使用的内存。
双重释放会导致错误,当程序尝试释放已经释放并且可能已经重新分配的内存区域时,会发生双重释放错误。
内存泄露,是经常面试考察的问题,某些类型的内存泄漏,其中程序无法释放已变得无法访问的对象占用的内存,这可能导致OOM错误内存耗尽。
很多开源工具可以帮助分析Java的内存泄露问题。
2、GC Root作用
GC Root是很特殊的对象,在GC进行垃圾回收之前,要判断对象是否可以被回收。这里判断的标准需要有依据。就是一个对象如何确定是不是被引用,或者被使用。
在Java中,有一些特殊的对象称为GC垃圾回收根(GC Root)。 它们充当垃圾收集标记机制的根对象(GC Root Object)。
GC Root Object可以从Java堆的外部访问,也就是不受GC的自动回收管制。可以理解为有免死金牌的Java对象。GC这套法律体系约束不了它们。
3、GC Roots类型
那么有哪些类似可以作为GC Roots类型呢?我们来看一下下面的类型列表:
类型 | 说明 |
---|---|
System Class 类型 | 由引导程序/系统类加载器加载的类型。例如,rt.jar中的所有内容(如java.util。*)。 |
JNI Local 类型 | 本机原生代码中的局部变量,例如用户定义的JNI代码或JVM内部代码。 |
JNI Global 类型 | 本机原生代码中的全局变量,例如用户定义的JNI代码或JVM内部代码。 |
Thread Block 类型 | 从当前活动的线程块引用的对象。 |
Thread 类型 | 已经启动运行的线程 thread. |
Busy Monitor 类型 | 忙碌的监视器代码,调用了wait()或notify()或已同步的所有代码。 |
Java Local 类型 | 局部变量。例如,输入参数或仍在线程堆栈中的方法的局部创建对象。 |
Native Stack 类型 | 本机原生代码中的输入或输出参数,例如用户定义的JNI代码或JVM内部代码。 |
Finalizable 类型 | 可终结对象,队列中等待其终结器运行的对象. |
Unfinalized 类型 | 未终结对象,具有finalize方法但尚未完成且尚未在终结器队列中的对象. |
Unreachable 类型 | 不可达对象,无法从任何其他根访问的对象,MAT使用. |
Java Stack Frame 类型 | Java栈框架,持有局部变量。解析Dump时使用. |
Unknown 类型 | 根类型未知对象。MAT分析Dump文件时的特殊类型,归类不可知类型. |
像系统类型、JNI代码、运行的线程、同步区的对象等还是比较好理解的,基本是不受管控。Finalizable、Unfinalized和Unreachable都是GC回收中比较特殊的对象。剩下的Unknown可以理解为MAT分析过程中其他不可知对象准备的Root类型。
参考资料:
1、https://help.eclipse.org/2020-03/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Fconcepts%2Fgcroots.html&cp=37_2_3
2、https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html