亲宝软件园·资讯

展开

Java虚拟机 浅析Java虚拟机详解之概述、对象生存法则

小游子YKY 人气:0
想了解浅析Java虚拟机详解之概述、对象生存法则的相关内容吗,小游子YKY在本文为您仔细讲解Java虚拟机的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:Java虚拟机,Java虚拟机对象生存法则,Java虚拟机概述,下面大家一起来学习吧。

Java与C++之间有一堵由内存分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来。

垃圾搜集器与内存分配策略.png

一、概述

Java堆和方法区这两个区域有着很显著的不确定性:

1、一个接口的多个实现类需要的内存可能会不一样,一个方法所执行的不同条件分支所需要的内存也可能不一样
2、只有处于运行期间,我们才能知道程序究竟会创建哪些对象,创建多少个对象,这部分内存的分配和回收是动态的

垃圾收集器所关注的正是这部分的内存该如何管理

2020072320140535.png

二、对象已死?

1、引用计数法

在对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加一;当引用失效时,计数器就减一;任何时刻计数器为零的对象是不可能再被使用的。

引用计数器虽然占用了一些额外的内存空间来进行计数,原理简单,判定效率很高;

为什么主流Java虚拟机没有使用引用计数器来管理内存呢

引用计数法看似简单的算法有很多例外情况要考虑,必须配合大量额外处理才能保证正确的工作,比如单纯的引用计数很难解决对象之间互相循环引用的问题

引用计数器的缺陷

/**
 * @Author: yky
 * @CreateTime: 2020-12-13
 * @Description: 引用计数器的缺陷
 */
public class ReferenceCountingGC {
 public Object instance = null;
 private static final int _1MB = 1024 * 1024;
 /**
  * 这个成员变量唯一作用是占内存
  */
 private byte[] bigSize = new byte[2 * _1MB];
 public static void testGC(){
  ReferenceCountingGC objA = new ReferenceCountingGC();
  ReferenceCountingGC objB = new ReferenceCountingGC();
  objA.instance = objB;
  objB.instance = objA;
  //发生GC,objA、objB能否被回收
  System.gc();
 }
}

运行代码收查看日志信息发现,这两个对象均被回收虚拟机并没有因为这两个相互引用就放弃回收他们---->Java虚拟机并不是通过计数算法来判断对象是否存活的;

2、可达性分析算法

该算法的核心思想:通过一系列称为“GC Roots”的根对象作为起始节点集,从这些结点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连(图论话来说从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的)

image

对象obj5obj6obj7虽然有关联,但是他们到GC roots不可达因此他们会被判定为可回收对象

在 Java 语言中,可作为 GC Roots 的对象包括以下几种

无论通过哪种算法判断对象是否存活都和“引用”离不开关系。

1)强引用

是指在程序代码之间普遍存在的引用赋值,Object obj = new Object();这种引用关系。
无论什么情况下,只要强引用关系还在,垃圾收集器就不会回收掉被引用的对象;

2)软引用

用来描述一些还有用,但非必须的对象。只要软引用关联着的对象,在系统将要发生内存溢出前,会把这些对象列进回收范围之中进行第二次回收;如果这次的回收还没有足够的空间,才会抛出内存溢出的异常;

JDK1.2后提供SoftReference类实现软引用:

Soft reference objects, which are cleared at the discretion of the garbage
collector in response to memory demand. Soft references are most often used
to implement memory-sensitive caches.

3)弱引用

弱引用也被用来描那些非必须对象,强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止,当垃圾收集器开始工作,无论当前内存是足够,都会回收掉只被弱引用关联的对象;

JDK1.2后WeakReference类用来实现弱引用:

Weak reference objects, which do not prevent their referents from being

made finalizable, finalized, and then reclaimed. Weak references are most

often used to implement canonicalizing mappings.

4)虚引用

也叫“幽灵引用”、“幻影引用”,最弱的一种引用关系

PhantomReference类来实现虚引用:

Phantom reference objects, which are enqueued after the collector determines that their referents may otherwise be reclaimed. Phantom references are most often used to schedule post-mortem cleanup actions.

应用需要读取大量本地图片

如果每次读取图片都从硬盘读取,则会严重影响性能;解决方案:【软引用或者弱引用】

Map<String,SoftReference<BitMap>> imp = new HashMap<String,SoftReference<BitMap>>

4、生存还是死亡?

在进行过可达性分析后的对象也不一定是非死不可的,该对象进行可达性分析后,发现没有与GC Roots相连接的引用链

/**
1.对象可以在GC时自救
2.自救的办法只有一次,因为一个finalize方法最多只能被调用一次
**/
public class FinalizeEscapeGC {

 public static FinalizeEscapeGC SAVE_HOOK = null;
 public void isAlive(){
  System.out.println("yes,I am still alive :)");
 }

 @Override
 protected void finalize() throws Throwable {
  super.finalize();
  System.out.println("finalize method executed !");
  FinalizeEscapeGC.SAVE_HOOK = this;
 }

 public static void main(String [] args) throws InterruptedException {
  SAVE_HOOK = new FinalizeEscapeGC();
  //对象第一次成功拯救自己
  SAVE_HOOK = null;
  System.gc();
  //因为finalize优先级很低,所以延迟0.5s以等待它;
  Thread.sleep(500);
  if(SAVE_HOOK != null){
   SAVE_HOOK.isAlive();
  }else{
   System.out.println("no, i am dead :(");
  }

  //下面这段代码再执行一遍,验证对象是不是可以成功
  SAVE_HOOK = null;
  System.gc();
  Thread.sleep(500);
  if(SAVE_HOOK != null){
   SAVE_HOOK.isAlive();
  }else{
   System.out.println("no, i am dead :(");
  }
 }

}

结果如下:

finalize method executed !
yes,I am still alive :)
no, i am dead :(

5、回收方法区

很多人认为方法区(或者HotSpot虚拟机中的元空间或永久代)是没有垃圾收集行为的,《Java虚拟机规范》中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在方法区进行垃圾收集的“性价比”一般比较低:在堆中,尤其是在新生代中,常规应用进行一次垃圾收集一般可以回收70%~95%的空间,而永久代的垃圾收集效率远低于此。

方法区的垃圾收集主要回收两部分:废弃的常量和不再使用的类型;

假如一个字符串“Java”已经进入了常量池中,但是当前系统没有任何一个字符串对象的值是“Java”,换句话说是没有任何String对象引用常量池中的“Java”常量,也没有其他地方引用了这个字面量,如果在这时候发生内存回收,而且必要的话,这个“Java”常量就会被系统清理出常量池。

判定一个常量是否是“废弃常量”比较简单。而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面3个条件才能算是“无用的类”:

  1. 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。
  2. 加载该类的ClassLoader已经被回收。
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是“被允许”,而不是和对象一样,不使用了就必然会回收。在大量使用反射、动态代理、CGLib等bytecode框架的场景,以及动态生成JSP和OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证不会被方法区造成过大的内存压力。

加载全部内容

相关教程
猜你喜欢
用户评论