上一篇文章中讨论了Java内存运行时的各个区域,其中程序计数器、虚拟机栈、本地方法栈随线程生灭,且创建时需要多少内存,基本上在译期间就决定的了,所以在内存回收时无需特殊的关注。而堆和方法区则不同,首先堆中只能在运行时,随着方法的调用而确定创建哪些对象;方法区中也同样如此,常量池中的常量、加载的类信息也是随时在发生着变化且不可预知。所以说,JVM内存回收,主要针对的是这两部分的内容。

1、堆中“死”对象

  笼统的说,没用的对象就是死对象。

1.1如何判定对象“已死”

1.1.1引用计数法

  给对象添加一个引用计数器,当有其他对象引用该对象时,计数器+1;当引用失效时,计数器-1。原理简单,实现容易,听起来也不错。但这个算法无法解决对象间循环引用的问题。也就是说对象A引用了对象B,而对象B同时也引用了对象A,此时就形成了循环引用。这样两个对象就永远都不会被回收。主流的JVM中均没有采用这种算法。

1.1.2可达性分析

  基本思路是找一些对象作为遍历的起始点(成为GC Roots),从这些起始点开始搜索,当某个对象并没有和任何GC Root产生关联,则认为这个对象已经不被使用了,可以清除。通常,可作为GC Root的对象有以下几种:

    1)虚拟机栈,栈帧中的本地变量表中引用的对象

    2)方法区中加载的类的静态属性引用的对象

    3)方法区中常量引用的对象(疑问,方法区中的常量到底有哪些)

    4)本地方法栈中引用的对象

  由上可见,可作为GC Roots的对象基本上可以归类为全局性引用和执行上下文,而应用中这些对象实在太多,这就导致根节点的遍历(或者叫确定GC Roots)势必会消耗很多时间,那是如何确定GC Roots的呢?当前主流Java虚拟机使用的都是准确式GC,以HotSpot虚拟机为例,当系统确定GC Roots时,并不需要遍历整个方法区或者堆,而是知道哪些地方存放着对象的引用,这是有一个叫OopMap的数据结构来实现的。

1.2关于引用

  上文谈到的引用,是我们平时经常谈到的、最直白的“引用”。在JDK1.2之前,“引用”的定义是:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。这种定义非常的纯粹,非黑即白。在内存回收的时候也是,有用就留着,没用立刻删。但有一些场景,例如,当内存充足的时候,某块内存留着备用;内存不充足的时候,回收这块内存。这时纯粹的“引用”就没办法了。在JDK1.2之后,Java对引用进行了扩展,将引用分为强引用、软引用、弱引用、虚引用。

  1)强引用:就是上面说的纯粹的引用

String str=new String("abc"); 

  2)软引用:表示对象还有用,但不是必须的。内存不够用的时候,才会回收这些内存。软引用可用来实现内存敏感的高速缓存。

String str=new String("abc");                                     // 强引用
SoftReference<String> softRef=new SoftReference<String>(str); // 软引用

后续在垃圾回收时,JVM内部逻辑如下:

if(JVM.内存不足()) {
str = null; // 转换为软引用
System.gc(); // 垃圾回收器进行回收
}

  3)弱引用:表示对象还有用,但不是必须的,且不如软引用“硬”。只要发生垃圾回收,这些对象就会被回收。

String str=new String("abc");
WeakReference<String> weakRef = new WeakReference<String>(str);

后续在垃圾回收时,JVM内部逻辑如下:

str = null;

  4)虚引用:最弱的引用,一个对象是否存在虚引用,并不影响其生存。为一个对象设置虚引用的唯一目的是在该对象被回收时,能够收到一个系统通知。 虚引用主要用来跟踪对象被垃圾回收器回收的活动。

2、方法区内内存

  JDK1.8之前方法区是放到永久代中实现的(HotSpot虚拟机)。对于堆中的内存回收,尤其是新生代,回收率能够达到70%~95%;而永久代中的回收率则非常低。也就是说,回收方法区内的内存性价比很低。但这块内存又不能没有垃圾回收机制,SUN公司就曾公布过关于方法区内存泄漏的严重BUG。

  方法区中垃圾回收主要关注两部分:无用的常量和类。

  常量的回收与堆中对象的回收机制类似,当某个常量没有被任何对象引用的时候,这个常量就没有用了,就可以被回收。举例,当前系统中找不到任何String对象引用了常量池中的的某个字符串常量:abcd,那么abcd这个常量就会被回收。同理,常量池中其他类、方法、字段的符号引用也与此类似。

  回收类的效率非常低,但在当前企业级应用大量使用反射(Spring IOC,在bean注入的时候,通过反射实例化一个类,将其通过setter方法放到bean中)、动态代理(Spring AOP,AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。)、CGLib(CGLib技术后续继续学习)等技术的前提下,类的回收也变得很重要。已加载类的回收条件非常苛刻,需要满足以下三个条件,才有可能被JVM回收:

    1)该类产生的对象实例均被回收

    2)加载该类的ClassLoader已经被回收(类加载机制后续继续学习)

    3)该类的类对象没有在任何地方引用,无法反射出这个类的方法

JVM内存回收机制——哪些内存需要被回收(JVM学习系列2)的更多相关文章

  1. JVM自动内存管理机制——Java内存区域(下)

    一.虚拟机参数配置 在上一篇<Java自动内存管理机制——Java内存区域(上)>中介绍了有关的基础知识,这一篇主要是通过一些示例来了解有关虚拟机参数的配置. 1.Java堆参数设置 a) ...

  2. Chrome V8系列--浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略

    V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制.因此,V8 将内存(堆)分为新生代和老生代两部分. 一.前言 V8的垃圾回收机制:JavaScript使用垃圾回收机制来自动管理内存.垃圾 ...

  3. JS基础-垃圾回收机制与内存泄漏的优化

    [V8引擎]浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略 垃圾回收机制 如何判断回收内容 如何确定哪些内存需要回收,哪些内存不需要回收,这是垃圾回收期需要解决的最基本问题.我们可以这样 ...

  4. Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches Slab内存管理机制 SLUB内存管理机制

    Linux中的Buffer Cache和Page Cache echo 3 > /proc/sys/vm/drop_caches   Slab内存管理机制 SLUB内存管理机制 http://w ...

  5. GNU C - 关于8086的内存访问机制以及内存对齐(memory alignment)

    一.为什么需要内存对齐? 无论做什么事情,我都习惯性的问自己:为什么我要去做这件事情? 是啊,这可能也是个大家都会去想的问题, 因为我们都不能稀里糊涂的或者.那为什么需要内存对齐呢?这要从cpu的内存 ...

  6. JVM和GC垃圾回收机制和内存分配

    JVM运行期间 线程共享 线程私有 线程共享: 方法区 堆方法区:存放可以共享数据,静态常量,类的共有方法属性字段等,可以共享的存在方法区. 堆:存放class对象 . 线程私有:本地方法栈 虚拟机栈 ...

  7. JVM垃圾回收机制与内存回收

    暂时转于:https://blog.csdn.net/qq_27035123/article/details/72857739 垃圾回收机制 GC是垃圾回收机制,java中将内存管理交给垃圾回收机制, ...

  8. Java垃圾回收机制以及内存泄露

    1.Java的内存泄露介绍 首先明白一下内存泄露的概念:内存泄露是指程序执行过程动态分配了内存,可是在程序结束的时候这块内存没有被释放,从而导致这块内存不可用,这就是内存 泄露,重新启动计算机能够解决 ...

  9. JVM自动内存管理机制——Java内存区域(上)

    一.JVM运行时数据区域概述 Java相比较于C/C++的一个特点就是,在虚拟机自动内存管理机制的帮助下,我们不需要为每一个操作都写像C/C++一样的delete/free代码,所以也不容易出现内存泄 ...

随机推荐

  1. MVC:添加Html辅助器

    本文的方法来自 <精通asp.net Mvc5>. 一.添加视图模型 为了支持Html辅助器方法,可以把可用页面数,当前面,以及存储库中产品数等信息传递给视图.在Model文件夹内增加一个 ...

  2. c#学习个人总结

    c#一门程序语言,我的专业课.有了一学期的接触对它的了解还只是皮毛,里面有许多的知识都有待了解学习和提高,熟练掌握程序的语法,能够熟练的调用,对于一些理论性的知识还需要今后更多的揣摩和研究,讲书本中的 ...

  3. (84)Wangdao.com第十八天_JavaScript Promise 对象

    Promise 对象 是 JavaScript 的异步操作解决方案,为异步操作提供统一接口. 目前 JavaScript 原生支持 Promise 对象 它起到代理作用(proxy),充当异步操作与回 ...

  4. Ajax_简介: 异步的 JS 和 XML_原生写 ajax 分析其原理_jquery_ajax_art-template

    AJAX Asynchronous JavaScript And XML 通过 AJAX 可以在 浏览器中向 服务器 发送异步请求 一种 使用现有标准的 新方法,而非新语言 XML  可扩展标记语言 ...

  5. Nginx 自定义404、500、502 页面

    利用nginx的反向代理来实现 服务器404 和500 等状态码的自定义页面 1.nginx配置文件 nginx.conf 配置开启代理错误拦截 和配置页面  下划线部分  http { ...... ...

  6. HttpClient参观记:.net core 2.2 对HttpClient到底做了什么?

    .net core 于 10月17日发布了 ASP.NET Core 2.2.0 -preview3,在这个版本中,我看到了一个很让我惊喜的新特性:HTTP Client Performance Im ...

  7. .NET Core 中基于 IHostedService 实现后台定时任务

    .NET Core 2.0 引入了 IHostedService ,基于它可以很方便地执行后台任务,.NET Core 2.1 则锦上添花地提供了 IHostedService 的默认实现基类 Bac ...

  8. Windows10 ntoskrnl.exe占用大量的磁盘空间(100%)

    一.解决办法: 1.此电脑(右键)>  管理(点击)> 系统工具 > 任务计划程序 > 任务计划程序库 > Microsoft > windows > .NE ...

  9. C++ MFC万能的类向导

    MFC的类向导 只要你掌握了类向导,你基本就已经掌握了MFC了,毕竟布局和代码都是自动生成,再加上C++基础上手还是挺快的,剩下的就是多多练习了. 转自: https://blog.csdn.net/ ...

  10. Cocos Creator cc.Button (脚本事件内容)

    cc.Class({extends: cc.Component,properties: {}, onLoad: function () { var clickEventHandler = new cc ...