程序计数器,虚拟机栈和本地方法栈

  首先我们先来看下垃圾回收中不会管理到的内存区域,在Java虚拟机的运行时数据区我们可以看到,程序计数器,虚拟机栈,本地方法栈这三个地方是比较特别的。这个三个部分的特点就是线程私有的,它们随着线程的创建而诞生,也因线程的结束而灭亡。栈中的栈帧随着方法的进入和退出会有条不絮的执行着进栈和出栈。每一个栈帧中分配多少内存,基本上是在类结构确认下来的时候就已知的,因此这几个区域的内存分配和回收都具备确定性,在这几个区域内就不需要过多考虑回收的问题,因为方法结束或者线程结束,内存自然就跟随着回收了。

Java堆和方法区

  我们讨论的垃圾回收,主要就是关于Java堆中废弃对象的回收。Java堆和方法区中,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们会在程序运行的时候动态创建对象,这部分内存的分配和回收也是动态的,垃圾收集器所关注的是这部分内存如何进行回收。

对象状态判断

  在研究对象的回收之前,我们需要先看一下如何进行判断对象是否还有存活价值,即要先判断对象是否还有被引用,这是我们进行垃圾回收的第一步,判断对象存活状态。接下来我会讲一下几种判断的方法。

   1.、引用计数法

       一个比较通俗的方法就是当对象在创建的时候,就给对象创建一个对象计数器,每当有一个地方引用到这个对象的时候,计数器加一;当引用失效的时候,计数器减1;任何时候计数器为0的对象就是不可能被使用的,就是我们所认知的 --死亡对象。 客观地说,引用计数算法的实现比较简单,判定效率也很高,在大部分情况下它都是一个不错的算法,也有一些比较著名的应用案例,例如微软公司的COM(Component Obejct Mode)技术,使用ActionScript3的FlashPlayer等技术都引用了技术算法进行内存管理。 但是,至少主流的Java虚拟机里面没有用到引用计数算法来管理内存,之中最主要的问题就是他很难解决对象之间相互循环引用的问题:

 

public class ReferenceGc {
public Object instance = null;
public static void main(String[] args){
ReferenceGc gcA = new ReferenceGc();
ReferenceGc gcB = new ReferenceGc();
gcA.instance=gcB;
gcB.instance=gcA;
gcA=null;
gcB=null; System.gc();
}
}

    这里面如果采用的是引用计数算法来进行垃圾回收的话,这种对象明明是没有使用,但是却仍然占内存,在Java中,这种情况是非常常见的,所以这种算法并不能解决Java中对象相互引用的问题,所以Java虚拟机判断对象存活状态的算法是选择了接下来介绍的--可达性分析算法。

    2、可达性分析算法

                

      这个算法的基本思想是,通过一系列的GC Roots 的对象作为七点,从这些节点开始的向下搜索,搜索所走过的路径成为引用连,当一个对象到GC Roots 没有任何引用链相连时,则证明此对象时不可用的。 如上图所示,虽然Object5,Object6和Object7相互有引用,但是他们与GC Roots间是不可达的,所以它们将会被判定为使可回收的对象。

      大家可能会很疑惑,那为什么Object5为什么不能当GC Roots呢?

         首先,我们要规定好GC Roots的定义范围,并不是说所有的对象都能够成为GC Roots:

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

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

             - 方法区中类静态属性引用的对象

             - 方法区中常量引用的对象

             - 本地方法栈中JNI引用的对象

对象在被回收前最后的自我救赎

  

    上面我们说完了Java虚拟机中判断对象存活状态的算法,那么是不是说我们判断出对象时没有(有效)引用之后就会被马上回收呢?实际上并不是这样的,即时在可达性分析分算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段。

      对象要被宣告死亡要经历两个阶段:

           第一个阶段就是可达性分析,判断对象是否具有有效引用

           第二个阶段就是判断对象是否有必要执行 finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况称为“没有必要执行”,就是对象已经没有任何机会完成救赎。

package com.Shop.Test;

/**
* Created by Administrator on 2016/7/25.
*/
public class FinalizeEscapeGc {
private 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();
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

      我们可以看到,在对象“死亡”之后,并没有马上死亡,我们覆盖了父类的finalized 方法,这时候就会给对象一次救赎的机会,但是也就仅此一次,当对象第二次“死亡”的时候,我们发现,即使是覆盖了finalized方法也没有办法再去拯救这个对象了。

      从上面的例子我们可以很直白的理解这个finalized方法在垃圾回收中起到的作用,那就是一次且仅一次救赎的机会。

方法区的回收

   实际上方法区还是会进行垃圾回收,但是效率远远比不上堆中垃圾的回收,因此在垃圾回收中经常会被忽略掉。方法区中,垃圾收集的主要内容分为两部分:废弃常量和无用的类

     1. 废弃常量

        这部分我们会比较容易理解,就举一个简单的例子,String str= "abc";这时候abc就已经进入了常量池中,当str改变类值,那么久没有对象引用"abc"这个常量,这时候,"abc"就会被系统清理出常量池。常量池中的其他类、方法、字段的符号引用也是如此

    

    2. 无用的类

        判定一个类是否是无用的类的条件相对苛刻许多。类需要同时满足以下三个条件才能算是“无用的类”

            1。该类所有的实例都已经被回收,也就是Java堆中已经不存在该类的任何实例

            2。加载该类的ClassLoader已经被回收

            3。该类对象的Java.lang.Class 对象没有在任何地方呗引用,无法再任何地方通过反射访问该类

       虚拟机可以对满足上述3个条件的无用类对象进行回收,这里说的仅仅是“可以”,行不是和对象一样,不适用就必然回收。

      

       需要对HotSpot虚拟机的参数进行设置,控制回收。

       一般情况下我们不会对方法区的无用类进行回收,但是在大量使用反射,动态代理、CGLib等ByteCode框架、这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。

深入Java虚拟机--判断对象存活状态的更多相关文章

  1. java虚拟机判断对象是否存活的方式

    引用计数算法:   给对象添加一个引用计数器,每当有地方应用时,计数器值就加一,当引用失效时,程序计数器就减一,只要引用计数器的值为零时,就表示对象不可能再被引用,例如微软的 component ob ...

  2. Java虚拟机判定对象存活算法

    1.引用计数算法 描述:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器值为0的对象就是不可能再被使用的. 特点:实现简单,判定效率高. ...

  3. Java 垃圾回收机制方法,判断对象存活算法

    垃圾回收机制: 不定时去堆内存中清理不可达对象.不可达的对象并不会马上就会直接回收, 垃圾收集器在一个Java程序中的执行是自动的,不能强制执行,即使程序员能明确地判断出有一块内存已经无用了,是应该回 ...

  4. JVM-如何判断对象存活与否与CMS收集器和G1收集器的区别

    JVM如何判断对象存活? 1.计数器 2.可达性分析   (很多主流语言采用这种方法来判断对象是否存活) 计数器:每当有一个地方引用该对象时,计数器 +1:引用失效则 -1: 优点:实现简单,判定效率 ...

  5. JVM高级特性-三、垃圾收集之判断对象存活算法

    一.概述 运行时数据区中,程序计数器.虚拟机栈.本地方法栈都是随线程而生随线程而灭的 因此,他们的内存分配和回收是确定的,在方法或线程结束时就回收.而Java堆和方 法区则是不确定的,程序运行过程中创 ...

  6. JAVA虚拟机之对象探秘

    上一章主要写到了JVM中运行时数据区域各个部分的功能及其作用.上一章说到了对象是分配在堆上面的,所以接下来我们写到对象在堆内存中是如何创建.如何布局.如何访问.1. 对象的创建 在java程序中对象的 ...

  7. Java 虚拟机的对象创建

    堆中存储的内容:在程序运行时,动态创建的对象. 创建对象的四种方式:new,clone(浅复制),反射,反序列化. 浅复制:只能复制当前对象本身,如果当前对象(A)引用了另外的对象(B),则引用对象( ...

  8. Java虚拟机(二)-对象创建

    这一篇大致说明一下,对象在Java堆中对象分配.内存布局以及访问定位 1.对象的创建 虚拟机在遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引 ...

  9. Java虚拟机学习 - 对象访问

    对象访问会涉及到Java栈.Java堆.方法区这三个内存区域. 如下面这句代码: Object objectRef = new Object(); 假设这句代码出现在方法体中,"Object ...

随机推荐

  1. 浅谈WEB页面提速(前端向)

    记得面试现在这份工作的时候,一位领导语重心长地谈道——当今的世界是互联网的世界,IT企业之间的竞争是很激烈的,如果一个网页的加载和显示速度,相比别人的站点页面有那么0.1秒的提升,那也是很大的一个成就 ...

  2. 基于DDD的现代ASP.NET开发框架--ABP系列文章总目录

    ABP相关岗位招聘:给热爱.NET新技术和ABP框架的朋友带来一个高薪的工作机会 ABP交流会录像视频:ABP架构设计交流群-7月18日上海线下交流会的内容分享(有高清录像视频的链接) 代码自动生成: ...

  3. C++中的命名空间

    一,命名空间(namespace)的基本概念以及由来 1.什么是标识符: 在C++中,标识符可以是基本的变量,类,对象,结构体,函数,枚举,宏等. 2.什么是命名空间: 所谓的命名空间是指标识符的可见 ...

  4. 创建几个常用table展示方式插件

    这次和大家分享的是自己写的一个table常用几种展示格式的js插件取名为(table-shenniu),样式使用的是bootstrap.min.css,还需要引用jquery.min.js包,这个插件 ...

  5. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  6. 【NLP】干货!Python NLTK结合stanford NLP工具包进行文本处理

    干货!详述Python NLTK下如何使用stanford NLP工具包 作者:白宁超 2016年11月6日19:28:43 摘要:NLTK是由宾夕法尼亚大学计算机和信息科学使用python语言实现的 ...

  7. [原] KVM 虚拟化原理探究(2)— QEMU启动过程

    KVM 虚拟化原理探究- QEMU启动过程 标签(空格分隔): KVM [TOC] 虚拟机启动过程 第一步,获取到kvm句柄 kvmfd = open("/dev/kvm", O_ ...

  8. golang语言构造函数

    1.构造函数定义 构造函数 ,是一种特殊的方法.主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中.特别的一个类可以有多个构造函数 ,可根据其参数个 ...

  9. mono for android学习过程系列教程(4)

    今天要讲的事情是构建安卓程序的UI界面. 首先给大家上点小点心,如图: 上面就是我们界面的设计模块,仔细看中间大块的下方,有一个Source,这就类似webform里面的设计和源代码界面. 在这个页面 ...

  10. Python 黑帽编程大纲(变化中)

    Python 黑帽编程大纲(预览版) 教程说明: 本系列教程,采用的大纲母本为<Understanding Network Hacks Attack and Defense with Pytho ...