为了账号安全,请及时绑定邮箱和手机立即绑定

想了解一下,在Java中什么样的对象才能作为gc root,gc roots有哪些?

/ 猿问

想了解一下,在Java中什么样的对象才能作为gc root,gc roots有哪些?

慕后森 2019-11-10 13:09:38

Java中什么样的对象才能作为gc root,gc roots有哪些


查看完整描述

3 回答

?
炎炎设计

GC在什么时候对什么做了什么?
要回答这个问题,先了解下GC的发展史、jvm运行时数据区的划分、jvm内存分配策略、jvm垃圾收集算法等知识。
先说下jvm运行时数据的划分,粗暴的分可以分为堆区(Heap)和栈区(Stack),但jvm的分法实际上比这复杂得多,大概分为下面几块:
1、程序计数器(Program Conuter Register)
程序计数器是一块较小的内存空间,它是当前线程执行字节码的行号指示器,字节码解释工作器就是通过改变这个计数器的值来选取下一条需要执行的指令。它是线程私有的内存,也是唯一一个没有OOM异常的区域。
2、Java虚拟机栈区(Java Virtual Machine Stacks)
也就是通常所说的栈区,它描述的是Java方法执行的内存模型,每个方法被执行的时候都创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等。每个方法被调用到完成,相当于一个栈帧在虚拟机栈中从入栈到出栈的过程。此区域也是线程私有的内存,可能抛出两种异常:如果线程请求的栈深度大于虚拟机允许的深度将抛出StackOverflowError;如果虚拟机栈可以动态的扩展,扩展到无法动态的申请到足够的内存时会抛出OOM异常。
3、本地方法栈(Native Method Stacks)
本地方法栈与虚拟机栈发挥的作用非常相似,区别就是虚拟机栈为虚拟机执行Java方法,本地方法栈则是为虚拟机使用到的Native方法服务。
4、堆区(Heap)
所有对象实例和数组都在堆区上分配,堆区是GC主要管理的区域。堆区还可以细分为新生代、老年代,新生代还分为一个Eden区和两个Survivor区。此块内存为所有线程共享区域,当堆中没有足够内存完成实例分配时会抛出OOM异常。
5、方法区(Method Area)
方法区也是所有线程共享区,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。GC在这个区域很少出现,这个区域内存回收的目标主要是对常量池的回收和类型的卸载,回收的内存比较少,所以也有称这个区域为永久代(Permanent Generation)的。当方法区无法满足内存分配时抛出OOM异常。
6、运行时常量池(Runtime Constant Pool)
运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。

垃圾收集(Garbage Collection)并不是Java独有的,最早是出现在Lisp语言中,它做的事就是自动管理内存,也就是下面三个问题:
1、什么时候回收
2、哪些内存需要回收
3、如何回收

1、什么时候回收?
上面说到GC经常发生的区域是堆区,堆区还可以细分为新生代、老年代,新生代还分为一个Eden区和两个Survivor区。
1.1 对象优先在Eden中分配,当Eden中没有足够空间时,虚拟机将发生一次Minor GC,因为Java大多数对象都是朝生夕灭,所以Minor GC非常频繁,而且速度也很快;
1.2 Full GC,发生在老年代的GC,当老年代没有足够的空间时即发生Full GC,发生Full GC一般都会有一次Minor GC。大对象直接进入老年代,如很长的字符串数组,虚拟机提供一个-XX:PretenureSizeThreadhold参数,令大于这个参数值的对象直接在老年代中分配,避免在Eden区和两个Survivor区发生大量的内存拷贝;
1.3 发生Minor GC时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于,则进行一次Full GC,如果小于,则查看HandlePromotionFailure设置是否允许担保失败,如果允许,那只会进行一次Minor GC,如果不允许,则改为进行一次Full GC。

2、哪些内存需要回收
jvm对不可用的对象进行回收,哪些对象是可用的,哪些是不可用的?Java并不是采用引用计数算法来判定对象是否可用,而是采用根搜索算法(GC Root Tracing),当一个对象到GC Roots没有任何引用相连接,用图论的来说就是从GC Roots到这个对象不可达,则证明此对象是不可用的,说明此对象可以被GC。对于这些不可达对象,也不是一下子就被GC,而是至少要经历两次标记过程:如果对象在进行根搜索算法后发现没有与GC Roots相连接的引用链,那它将会第一次标记并且进行一次筛选,筛选条件是此对象有没有必要执行finalize()方法,当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用执行过一次,这两种情况都被视为没有必要执行finalize()方法,对于没有必要执行finalize()方法的将会被GC,对于有必要有必要执行的,对象在finalize()方法中可能会自救,也就是重新与引用链上的任何一个对象建立关联即可。

3、如何回收
选择不同的垃圾收集器,所使用的收集算法也不同。
在新生代中,每次垃圾收集都发现有大批对象死去,只有少量存活,则使用复制算法,新生代内存被分为一个较大的Eden区和两个较小的Survivor区,每次只使用Eden区和一个Survivor区,当回收时将Eden区和Survivor还存活着的对象一次性的拷贝到另一个Survivor区上,最后清理掉Eden区和刚才使用过的Survivor区,Eden和Survivor的默认比例是8:1,可以使用-XX:SurvivorRatio来设置该比例。
而老年代中对象存活率高,没有额外的空间对它进行分配担保,必须使用“标记-清理”或“标记-整理”算法。



查看完整回答
反对 回复 2019-11-16
?
MM们

›在VisualBasic的程序设计中,面向对象是非常重要的编程概念。设计VisualBasic应用程序的过程,实际上是处理对象的过程。在编程过程中,可以使用由VisualBasic提供的对象,如窗体、控件和数据访问对象,也可以在应用程序中控制其它应用程序对象,甚至可以创建自己的对象,并且为它们定义附加的属性或方法。
›对象是可以作为单元处理的代码和数据的组合。对象可以是应用程序的片段,如控件或窗体,也可以是整个应用程序。›
VisualBasic中的每个对象都是由一个类来定义的。类相当于模子,它决定了每个铸件的特征,例如大小和形状。利用同一个模子,可以铸造出许多铸件。而对象就是铸件。
›虽然在实际编程的过程中,经常忽略类和对象的区别,但还是应该注意以下的两个概念:
工具箱上的控件代表的是类,控件是要等到它们在窗体上绘制出来之后才存在的。在建立一个控件的时候,实际上是建立的控件类的一个实例,这个实例才是在应用程序中要控制的对象。
在设计时,处理的窗体实际上是一个类。在运行时,VisualBasic才产生窗体的对象。
›对象具有自己的属性、方法和事件。属性窗口中列出了当前应用程序中的对象的类和属性。
›对象的属性是对象自己所封装的一些数据,用于定义对象自身的外观和相应的一些性质。对象的方法实际上是对象已经封装好的一段程序,它能够完成一定的功能,例如完成对对象自身的一些修改和调整等。对象的事件实际上也是一种数据类型,它通过接受系统传递一个应用程序的消息,从而根据用户的操作或者应用程序的运行做出相应的反应。›处理对象的过程,就是对对象的属性赋值,使用对象的方法和利用对象的事件,控制对象的外观和行为和对用户的操作做出反应的过程。›
对象提供了用户不必自己去编写的程序代码,例如:用户可以创建自己的对话框,但实际上并不需要这样做,而是利用VisualBasic提供的常用对话框控件。

查看完整回答
反对 回复 2019-11-16
?
德玛西亚99

关于对象到底是死是活,java采用的是可达性分析算法,如果一个对象到GC Roots对象(关于哪些对象可以作为GC Roots对象不再详细说明)不可达,就会作为GC回收的对象,如果到GC Roots可达,那么就还没死,不会回收,但是即使到GC Roots对象不可达,对象也还有自我救赎的机会,也并非死亡,如果重写了finlize方法,并且重新指向该对象,该对象还是存活,不会死亡,如果这个自我救赎的机会也错失,那么一般都会被回收掉

查看完整回答
反对 回复 2019-11-16

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信