Java虚拟机--对象回收

上一篇:Java虚拟机--Java堆中对象的创建和布局

哪些内存需要回收?

程序计数器、虚拟机栈、本地方法栈三个区域随线程而生,随线程而灭,这几个区域的内存分配和回收都具备确定性,不需要过多考虑回收问题,因为方法结束或线程结束时,内存自然就跟着回收了。而Java堆方法区则不一样:一个接口的多个实现类需要的内存可能不一样,一个方法中多个分支需要的内存也可能不一样,只有在程序处于运行时才会知道要创建哪些对象,这部分内存的分配和回收都是动态的,垃圾回收器关注的是这部分内存。

怎么判断一个对象要被回收?

引用计数法

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

引用计数法实现简单,判定效率也很高,在大部分情况下是一个不错的算法。但主流的Java虚拟机都没有使用引用计数法来管理内存,其中主要的原因是:它很难解决对象之间互相引用的问题。

可达性分析法

通过一系列的称为“GC Roots"的对象作为起点,从这些起点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(图论中的不可达)时,则证明该对象是不可用的。主流实现都是使用可达性分析实现判断对象是否存活的。

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

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象;
  • 方法区中类静态属性引用的对象;
  • 方法区中常量引用的对象;
  • 本地方法栈中Native方法引用的对象。

引用:

上面两种方法都与”引用“有关,Java中引用分为四种:强引用、软引用、弱引用和虚引用。

  • 强引用:只要某个对象有强引用与之关联,JVM必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。
  • 软引用:软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。
  • 弱引用:弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。
  • 虚引用:最弱的一种引用关系。一个对象是否有需引用的存在完全不会对其生存时间构成影响,也无法通过需引用来取得一个对象实例。为一个对象设置一个需引用的唯一目的就是能够在这个对象被收集器回收时收到一个系统通知。

回收方法区:

方法区可以不实现垃圾收集,但也可以进行垃圾收集。永久代(方法区)的垃圾收集主要回收两方面的内容:废弃常量无用的类。回收废弃常量和回收Java堆中的对象非常类似。

判断一个废弃常量非常容易但判断一个类是”无用的类“则苛刻得多,必须满足下面三个条件:

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

内存分配与回收策略

Java的自动化内存管理最终可以归结为解决两个问题:给对象分配内存和回收分配给对象的内存。

对象的内存分配,往大的方向说就是在堆上分配,对象主要分配在新生代的Eden区上,如果启用了本地线程分配缓冲,将按线程优先在TLAB上分配,少数情况下也直接在老年代中分配。下面是一些最普遍的内存分配原则:

1、对象优先在Eden分配

大多数情况下对象在新生代 Eden区分配,如果Eden区没有足够空间进行分配,虚拟机将会发起一次Minor GC。

  • 新生代GC(Minor GC):指发生在新生代的垃圾收集动作,因为Java对象大多具有朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度比较快。
  • 老年代GC(Major GC / Full GC):指发生在年老代的GC,出现了Major GC,经常会伴随至少一次的Minor GC(但并非绝对)。Major GC一般比Minor GC慢10倍以上。

2、大对象直接进入老年代

所谓的大对象是指,需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组。大对象对内存分配来说是一个坏消息(更坏的消息是”朝生夕灭“的大对象,编程中应该尽量避免),经常出现的大对象容易导致内存还有不少空间时就要触发急啊急手机以获取足够的空间。

3、长期存活的对象将进入老年代

虚拟机给每个对象定义了一个对象年龄计数器。如果对象在经历过第一一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor中,并且对象年龄加一。对象在Survivor中每熬过一次Minor GC就让年龄加一。当年龄增加到一定程度(默认为15),就会被晋升到年老代中。

4、动态对象年龄判定

为了适应不同情况,虚拟机并不永远要求对象年龄达到指定的数值才晋升到年老代。如果在Survivor空间中相同年龄的所有对象综合大于Survivor空间的一半,那么大于等于该年龄的对象都要直接进入老年代。

下一篇:Java虚拟机--垃圾收集算法和垃圾收集器

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏你不就像风一样

你知道Java的四种引用类型吗

在Java中提供了四个级别的引用:强引用,软引用,弱引用和虚引用。在这四个引用类型中,只有强引用FinalReference类是包内可见,其他三种引用类型均为p...

1222
来自专栏Jaycekon

深入Java虚拟机--判断对象存活状态

程序计数器,虚拟机栈和本地方法栈 首先我们先来看下垃圾回收中不会管理到的内存区域,在Java虚拟机的运行时数据区我们可以看到,程序计数器,虚拟机栈,本地方法栈...

3176
来自专栏developerHaoz 的安卓之旅

你真的懂 Java 的内存管理和引用类型吗?

对于 Java 程序员来说,在 Java 虚拟机自动内存管理机制的帮助下,不再需要为每一个 new 操作去写对应的 delete/free 代码,不容易出现内存...

1002
来自专栏java初学

java — JVM调优

3679
来自专栏Fundebug

JavaScript正则表达式进阶指南

例如,正则表达式/F.*g/会匹配“以F开头,以g结尾的字符串”,因此可以匹配"Hello, Fundebug!"中的Fundebug,exec方法会返回一个数...

1906
来自专栏python学习之旅

算法学习笔记(四):堆排序

1543
来自专栏编程

Windows数据类型

Windows使用或定了很多新的数据类型,前面几节中我们已经领略到了。它们虽然多,但是都有规律可循,很多都是对C/C++中数据类型的简单加工,而且很容易“见名知...

1926
来自专栏互联网大杂烩

Java垃圾回收机制

在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾。JVM的一个系统级线程会自动释放该内存块。垃圾回收意味着程序不再需要的对象是"无用...

3085
来自专栏JMCui

终结 finalize()和垃圾回收(garbage collection)

1、为什么要有finalize()方法?     假定你的对象(并非使用new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道释放那些经由new分配的内存,...

3147
来自专栏吴伟祥

编码总结笔记 原

我们知道,在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一...

1346

扫码关注云+社区

领取腾讯云代金券