上一篇文章中讨论了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. 《Head First JavaScript》 学习笔记

    <scipt type="text/javascript" src"cookie.js"> </script>  //脚本署名方法 &l ...

  2. JS-对象的数据重复

    <title>01-对象中数据的重复</title> <script type="text/javascript"> var arr = []; ...

  3. JavaScript学习day1

    JavaScript 特点: javascript 是一种脚本语言,它的解释器被称为javascript引擎,JavaScript被发明用于在HTML网页上使用,给HTML 网页增加动态功能 由于ja ...

  4. 关于 vuex 的使用忠告

    第一.看明白这张图在说话 简单解释一下,actions接收到components的行为后actions请求api 等获取数据,提交到mutations,然后mutations中才改变state ,反映 ...

  5. linux --- Ansible篇

    ansible背景 1.什么是ansible? ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet.cfengine.chef.func.fabric)的优 ...

  6. windows redis 自动启动

    start.bat D: D:\dev\redis- redis-server.exe redis.conf redis.vbs createobject( 之后,把 start.bat 放在redi ...

  7. STA 463 Simple Linear Regression Report

    STA 463 Simple Linear Regression ReportSpring 2019 The goal of this part of the project is to perfor ...

  8. Mysqlbinlog解析工具

    Mysql的binlog日志作用是用来记录mysql内部增删改查等对mysql数据库有更新的内容的记录(对数据库的改动),对数据库的查询select或show等不会被binlog日志记录;主要用于数据 ...

  9. WSL(Windows Subsystem for Linux)笔记一安装与使用

    1.安装linux子系统 很简单直接在启动或关闭windows功能 中选择“适用于linux的windows子系统”,确定安装后重启即可,安装还是比较快的只用了几分钟. 也可以直接使用shell命令行 ...

  10. 2019-oo-第二单元总结

    2019-OO-第二单元总结 多线程电梯调度问题 思路综述 第一次作业 第一次作业是非常简单的傻瓜电梯,不需要考虑容量,不需要考虑调度策略,运用了基本的生产者消费者模型,而且生产者消费者模型也一直贯穿 ...