Java虚拟机之内存管理

原创 2017年09月19日 08:45:21

内存模型

一说到内存管理,首先需要了解它的内存模型。
虚拟机的内存模型在jdk1.8之后有了一些变化,我们分开来看,请看下图:

这里写图片描述

由图我们可以看出,jdk每个版本都会有新生代和老年代,唯一不同的是小于1.8的版本为永久代,而大于等于1.8的版本去掉了永久代,转为元空间(Meta Space)。

永久代也就是存储的数据区里面的方法区,如果程序在运行中发生PermSpace溢出,则说明永久代内存不够,需要调整JVM参数增加永久代内存空间。

jdk1.8以后出现了MetaSpace,它和永久代不同的是,它的内存空间是动态扩展的,当然我们也可以设置MaxMetadaSpace来设置最大元空间内存数量,也就是在1.8以后设置PermSize是无效的。

本文主要讲解jdk1.8以前得内存管理机制

垃圾回收机制

在现代编程语言中,对垃圾回收算法主要有两种方式:引用计数器和可达性分析。

引用计数器

引用计数的原理大致是这样的:为每一个创建的对象设置一个引用计数,每当对象被引用一次后,引用计数加1,当对象引用失效时,引用计数减1,当引用计数为0是说明没有任何对象引用了,即可释放该对象。

引用计数的一个弊端是,他无法解决对象间相互引用的问题,比如下面这段代码:

public class RefrenceCountingGC {

    private Object instance = null;

    public static void main(String[] args) {
        RefrenceCountingGC gc1 = new RefrenceCountingGC();
        RefrenceCountingGC gc2 = new RefrenceCountingGC();
        gc1.instance = gc2;
        gc2.instance = gc1;

        gc1 = null;
        gc2 = null;
        //假设采用引用计数算法,在这里发生GC,gc1和gc2能否被回收?
        System.gc();
    }
}

两个对象始终处于相互引用阶段,因为引用计数永远无法为0,因此就不能自动释放它。

可达性分析

为了解决引用计数出现的这些问题,可达性分析算法出现了。

在主流的编程语言中,java和c#都是通过可达性分析来判定对象是否存活的。

它的原理大致是:通过一系列的被称为“GC Roots”的对象作为起始点,然后从这些节点开始向下搜索,搜索的路径连成的一条线,我们称之为“引用链”,当前一个对象到GC Roots没有任何引用链,即我们说的这个对象不可达时,则说明该对象是可以被回收的,通过下图可以更好的理解:
这里写图片描述

当一个对象不可达时,并不能代码这个对象就能马上被回收,他会处于死缓状态,而一个对象真正要回收时,至少需要经历两次标记。第一次标记的前提条件是,看该对象是否覆盖了finalize()方法或者finalize()方法被虚拟机调用过,如果覆盖了finalize()方法并且虚拟机还没有调用过,这时会标记它,该对象还有机会存活,方法很多,比如在finalize()方法内存引用该对象。

如果进行第二次回收时,由于虚拟机已经调用过finalize()方法,就不会再调用他了,这时该对象就会真正宣告死亡了。

请看下面这段代码:

public class GCRoot {

    private static GCRoot instance = null;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalized执行");
        instance = this;
    }

    public static void main(String[] args) throws Exception{
        instance = new GCRoot();
        instance = null;
        System.gc();
        //因为finalize执行优先级较低,这里等待0.5秒
        Thread.sleep(500);
        if(null != instance){
            System.out.println("对象拯救成功!");
        }else{
            System.out.println("对象被释放!");
        }

        //和上面的代码一样,不会执行finalize方法,所以拯救失败
        instance = null;
        System.gc();
        if(null != instance){
            System.out.println("对象拯救成功!");
        }else{
            System.out.println("对象被释放!");
        }
    }
}
运行结果:
finalized执行
对象拯救成功!
对象被释放!

java引用

jdk1.2之前的引用很简单,这里我们不探讨,我们主要探讨jdk1.2之后的引用。

java中将引用分为了:强引用、软引用、弱引用和虚引用。

强引用

强引用在java程序中最常见的一种引用类型,类似Object o = new Object()这类引用,只要强引用还在,垃圾回收器就永远不会回收它。

软引用

软引用通常用来描述一些可以用但非必须的对象,在内存溢出之前会先回收掉软引用相关联的对象,如果回收后内存依然不够,则才会抛出内存溢出异常。

弱引用

弱引用用来描述一些非必须的对象,但是它的强度比软引用还有弱一些。被弱引用关联的对象只能存活到下次垃圾回收器工作之前,当垃圾回收器开始工作时,无论当前内存是否足够,都会回收掉被弱引用关联的对象。

虚引用

虚引用是最弱的一种引用类型,为对像设置虚引用关系的唯一目的就是能在这个对象被垃圾回收之时收到一个系统通知。

垃圾收集算法

1、标记-清除算法

这是最基础的一种垃圾收集算法,后续所有的算法都是在这个算法的基础上进行扩展。

通过算法的名字大致能够看出,该算法分为了“标记”和“清除”两个阶段:首先需要标记出需要回收的所有对象,待标记完成后清除掉所有标记过的对象。这个算法的不足主要有两个:一是性能问题,标记和清除两个阶段的性能都不高,二是标记清除后会产品不连续的大量内存碎片,内存碎片太多会导致下一次在需要分配占用大量内存的对象时,无法找到足够的连续碎片而不得不再一次触发垃圾收集动作。

2、复制算法

为了解决效率问题,复制算法出现了。这种算法会将可用内存区域划分为大小相同的两块,当需要垃圾回收时,会先将可用的对象复制到另一个内存区域,从而将当前区域一次性清除。这样做的好处是每次都将一整块内存区域清除掉,从而避免了大量的内存碎片出现。

目前主流的商用虚拟机大多是采用复制算法来回收新生代。

3、标记-整理算法

当对象的存活率较高时采用复制算法,效率就会很低,因此对于老年代一般不采用复制算法。

鉴于这种问题,一种称之为“标记-整理”算法的思路出现了。它和“标记-清除”算法一样,都需要先进行标记,但是它不会简单一次性清除标记的对象,而是将所有存活对象都移动到另一端,然后清除掉边界外的对象。

4、分代收集算法

分代收集其实就是将内存划分为几块,比如将java堆划分为新生代和老年代,根据不用年代采取最适合的算法,所以目前主流商用虚拟机都是采用这种方式。

版权声明:本文为博主原创文章,未经博主允许不得转载。

java虚拟机内存管理机制(一):JVM内存管理总结【分享】

近期看了看Java内存泄露的一些案例,跟原来的几个哥们讨论了一下,深入研究发现JVM里面还是有不少以前不知道的细节,这里稍微剖析一下。先看一看JVM的内部结构——如图所示,JVM主要包括两个子系统和两...
  • lengyuhong
  • lengyuhong
  • 2010年10月20日 10:55
  • 29341

深入理解java虚拟机笔记:自动内存管理

第2章 java内存区域 寄存器架构的cpu和栈架构的cpu
  • groovy2007
  • groovy2007
  • 2014年09月10日 16:01
  • 947

Java虚拟机内存管理

Java虚拟机在Java程序运行的时候为了更好的管理内存,把内存分为了若干个不同的内存区域。他们分别是方法区、虚拟机栈、本地方法栈、堆和程序计数器 将下来介绍这几个内存区 程序计数器 程序计数器区,...
  • rock93
  • rock93
  • 2017年03月10日 19:04
  • 218

深入理解Java虚拟机——自动内存管理机制

特此申明: 前段时间找工作所以看了这本书,整理的时候除了参考网上已有的笔记贴,加上自己整合的,可能和别人有雷同之处。不过无所谓啦,写出来自己看看,需要的朋友参考下,仅此而已。 一:Ja...
  • yingminxing
  • yingminxing
  • 2012年11月22日 10:23
  • 3268

深入理解JVM07--虚拟机类加载机制--类加载器、双亲委派模型

本文是基于周志明的《深入理解Java虚拟机》 虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部实现,以便让应用程序自己决定如何去获取所...
  • oChangWen
  • oChangWen
  • 2016年05月21日 23:59
  • 1915

windows虚拟内存管理

内存管理是操作系统非常重要的部分,处理器每一次的升级都会给内存管理方式带来巨大的变化,向早期的8086cpu的分段式管理,到后来的80x86 系列的32位cpu推出的保护模式和段页式管理。在应用程序中...
  • lanuage
  • lanuage
  • 2016年07月21日 09:22
  • 1951

java内存管理和垃圾回收机制

我们知道,java所谓的跨平台特性(hardware- and operating system-independence)是依赖于JVM的。也就是,JAVA代码写好了之后会被编译成java的字节,一...
  • u010223904
  • u010223904
  • 2015年08月28日 10:48
  • 2041

JAVA虚拟机的内存管理

  • 2012年03月23日 15:45
  • 371KB
  • 下载

JVM基础 之Java HotSpot虚拟机中的内存管理

转载:http://sishuok.com/forum/blogPost/list/339.html 1  简介          依托JavaTM 2平台的力量,标准版(J2SE...
  • sujiacheng_123
  • sujiacheng_123
  • 2016年12月30日 16:19
  • 654

深入理解java虚拟机(一)--自动内存管理机制

java虚拟机运行时数据区java虚拟机在执行java程序时会将其管理的内存划分为若干不同的数据区域: - 程序计数器(Program Counter Register) - java虚拟机栈...
  • lansedeyouling
  • lansedeyouling
  • 2016年04月19日 17:30
  • 277
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java虚拟机之内存管理
举报原因:
原因补充:

(最多只允许输入30个字)