`
lvwenwen
  • 浏览: 930509 次
  • 性别: Icon_minigender_1
  • 来自: 魔都
社区版块
存档分类
最新评论

jvm的内存分配及运行机制(转)

    博客分类:
  • Jvm
 
阅读更多

http://www.cnblogs.com/200911/p/3922704.html

 

VM运行时数据区域

根据《Java虚拟机规范(第二版)》的规定,JVM包括下列几个运行时区域:

我们思考几个问题:

1.jVM是怎么运行的?

2.JVM运行时内存是怎么分配的?

3.我们写的java代码(类,对象,方法,常量,变量等等)最终存放在哪个区?

VM运行时数据区域:

1.程序计数器(program Counter Register):

     是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的 方式去实 现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个 计数器来完成。

         由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核) 只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存 储,我们称这类内存区域为“线程私有”的内存。

         如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Natvie方法,这个计数器值则为空 (Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

2.java虚拟机栈(Java Virtual Machine Stacks):

里面存放的是本地变量表(存放了编译期可知的各种标量类型:Boolean,byte,char,short,int,float,long,double)、对象的引用(不是对象本身,仅仅是引用指针)、方法返回地址等。

虚拟栈中规定了两种异常状况:

  1. 如果线程请求的深度大于虚拟机所允许的深度,就会抛出stackoverflowerror异常,也就是栈溢出异常。在使用递归的调用方法的情况下,很容易抛出这个异常。
  2. 如果VM栈可以动态扩展,当扩展的时候无法申请到足够的内存空间,则抛出OutOfMemoryError异常,内存溢出。

3.本地方法栈(native method stacks)

   这块区域在jvm运行内存中职责就相对比较少了。只是执行Native 方法。如果这个区的内存不足也是会抛出StackOverflowError 和 OutOfMemoryError 异常。

4.java 堆

这块区域是jvm中最大的一块区域了,java堆是被所有线程所共享的,也是GC主要的回收区,在jvm启动的时候就创建了。java堆的唯一的目的就是存放对象实例(所有new出来的对象)绝大部分对象的实例都是在这块区域分配。

从图中可以看出heap中还可以分为新生代(Young Generation)和老年代(Old Generation)。下面看这个图:

  • 新 生代:GC每隔一段时间就会对新生代进行回收,在分配对象遇到内存不足的时候,先对新生代进行GC,当新生代GC后,无法满足内存空间的分配需求,才会对 整个对空间和方法区进行GC(FULL GC).而新生代又可以分为:一个Eden Space和两块相同大小的Survivor Space(s0,s1或From Survivor 和 To Survivor)正式图中所看到的。新生代中的E区和S区又有不同的职责。
    • E区:GC触发比较频繁的区域,存储的是新new的对象,几乎所有对象都经过E区,如果多次GC仍然有存活的对象,就把存活的对象放到S区。
    • S区:S区作为Eden区和old(老年代)的缓存。它是可以向老年代转移活动对象的实例.
  • 老年代:用于存放多次新生代GC仍然活着的对象,如缓存对象。新建的对象也有可能直接进入老年代,主要有两种情况:①.大对象,可通过启动参数设置-XX:PretenureSizeThreshold=1024(单位为字节,默认为0)来代表超过多大时就不在新生代分配,而是直接在老年代分配。②.大的数组对象,切数组中无引用外部对象。
  • 无 论对java堆如何划分,目的是为了更好的回收内存,或者是更快的分配内存;java的堆在物理空间上处于不连续的空间,但在逻辑上是连续的即可。虚拟机 堆内存空间是可扩展到的,可以通过-Xmx和-Xms控制,如果堆上无法分配内存空间,并且堆也无法再扩展到额时候,将会抛出 OutOfMemoryError异常。  

5.方法区(Mehod Area)

   方法区和堆一样也是线程共享的区域,它主要是用于存储被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据,不属于heap的一部分。 相对来说,GC行为在这个区域是相对比较少发生的,但并不是某些描述那样永久代不会发生GC。对于sun公司的HotSpot虚拟机来说。gc也会对这块 区域进行垃圾回收,这里的回收主要是常量池的回收和对类的卸载。

     如果细分方法区的里面有为运行时常量池(Runtime Constant Pool),它主要存储Class文件中的版本、字段、方法、接口等描述信息。还 有一项信息是常量表(constant_pool table)用于存放编译期已可知的常量,这部分内容将在类加载后进入方法区(永久代)存放。但是java语言并不要求常量一定只有编译期预先置入 Class的常量表的内容才能进入方法区常量池,运行期间才可以将新内容放入常量池(最典型的是String.intern()方法)

实战OutOfMemoryError:

除了程序计数器,其他在VMSpec中都描述了产生OutOfMemoryError(下称OOM)的情形,那我们就实战模拟一下,通过几段简单的代码,令对应的区域产生OOM异常以便加深认识,同时初步介绍一些与内存相关的虚拟机参数。

1.Java堆:

java 堆存放的是对象实例,因此只要不断建立对象,并且保证GCRoots到对象之间有可达路径即可产生OOM异常。测试中限制Java堆大小为20M,不可扩 展,通过参数-XX:+HeapDumpOnOutOfMemoryError让虚拟机在出现OOM异常的时候Dump出内存映像以便分析。

代码:

复制代码
package com.lp.ecjtu;

import java.util.ArrayList;


public class JVMTestDemo_heap {
    public static void main(String[]args){
        java.util.List<OOMObject>list = new ArrayList<OOMObject>();
        while(true){
            list.add(new OOMObject());
        }
    }
}
/**
 * VMArgs:-Xms20m-Xmx20m-XX:+HeapDumpOnOutOfMemoryError
 * @author Administrator
 *
 */
class OOMObject{
    
}
复制代码
运行结果:
java.lang.OutOfMemoryError:Javaheapspace
Dumpingheaptojava_pid3404.hprof...
Heapdumpfilecreated[22045981bytesin0.663secs]

 

 

 

垃圾收集GC(GarbageCollection,下文简称GC):

总 结下:其中程序计数器、VM栈、本地方法栈随线程而生,随线程而灭;栈中的帧随着方法的进入退出,有条不紊的进行的出栈和入栈操作;每一个帧中分配多少内 存,基本上是在Class文件生成时就已知的(可能会由JIT动态晚期编译进行一些优化,但大体上可以认为是编译期可知
的),因此这几个区域的内存的分配和回收具备很高的确定性,因此在这几个区域不需要过多考虑回收问题。而java堆和方法区(包括运行时常量池)则不一样,我们必须等到程序实际运行期间才能知道会创建那些对象,这部分内存的回收和分配是动态的。

GC的历史远远比java来的久,在1960年诞生于MIT的Lisp(是一门真正的使用内存冬天分配和垃圾回收集)的语言。当Lisp在胚胎时期,人们在GC需要做的3件事情:

  1. 哪些内存需要回收
  2. 什么时候需要回收
  3. 怎么样回收

方法区的回收:

      方法区即后文提到的永久代,很多人认为永久代是没有GC的,《Java虚拟机规范》中确实说过可以不要求虚拟机在这区实现GC,而且这区GC的“性价比”一般比较低:在堆中,尤其是在新生代,常规应用进行一次GC可以一般可以回收70%~95%的空间,而永久代的GC效率远小于此。虽然VMSpec不要求,但当前生产中的商业JVM都有实现永久代的GC,主要回收两部分内容:废弃常量与无用类。这两点回收思想与Java堆中的对象回收很类似,都是搜索是否存在引
用,常量的相对很简单,与对象类似的判定即可。而类的回收则比较苛刻,需要
满足下面3个条件:
   1.该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例。
   2.加载该类的ClassLoader已经被GC。
   3.该类对应的java.lang.Class对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法。

在大量使用反射、动态代理、CGLib等bytecode框架、动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景都需要JVM具备类卸载的支持以保证
永久代不会溢出。

垃圾收集算法

     最基础的搜集算法是“标记-清除算法”(Mark-Sweep),如它的名字一样,算法分层“标记”和“清除”两个阶段,首先标记出所有需要回收的对象,然后回收所有需要回收的对象,整个过程其实前一节讲对象标记判定的时候已经基本介绍完了。说它是最基础的收集算法原因是后续的收集算法都是基于这种思路并优化其缺点得到的。它的主要缺点有两个,一是效率问题,标记和清理两个过程效率都不高,二是空间问题,标记清理之后会产生大量不连续的内存碎片,空间碎片太多可能会导致后续使用中无法找到足够的连续内存而提前触发另一次的垃圾搜集动作。

    为了解决效率问题,一种称为“复制”(Copying)的搜集算法出现,它将用内存划分为两块,每次只使用其中的一块,当半区内存用完了,仅将还存活的对象复制到另外一块上面,然后就把原来整块内存空间一次过清理掉。这样使得每次内存回收都是对整个半区的回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存就可以了,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,未免太高了一点。

   现在的商业虚拟机中都是用了这一种收集算法来回收新生代,IBM有专门研究表明新生代中的对象98%是朝生夕死的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块较大的eden空间和2块较少的survivor空间,每次使用eden和其中一块survivor,当回收时将eden和survivor还存活的对象一次过拷贝到另外一块survivor空间上,然后清理掉eden和用过的survivor。SunHotspot虚拟机默认eden和survivor的大小比例是8:1,也就是每次只有10%的内存是“浪费”的。当然,98%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有10%以内的对象存活,当survivor空间不够用时,需要依赖其他内存(譬如老年代)进行分配担保(Handle Promotion)。

    复制收集算法在对象存活率高的时候,效率有所下降。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保用于应付半区内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。因此人们提出另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然一样,但后续步骤不是进行直接清理,而是令所有存活的对象一端移动,然后直接清理掉这端边界以外的内存。

   当前商业虚拟机的垃圾收集都是采用“分代收集”(Generational Collecting)算法,这种算法并没有什么新的思想出现,只是根据对象不同的存活周期将内存划分为几块。一般是把Java堆分作新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法,譬如新生代每次GC都有大批对象死去,只有少量存活,那就选用复制算法只需要付出少量存活对象的复制成本就可以完成收集。

分享到:
评论

相关推荐

    深入理解JVM内存结构及运行原理全套视频加资料

    2019最新深入理解JVM内存结构及运行原理(JVM调优)高级核心课程视频教程下载。JVM是Java知识体系中的重要部分,对JVM底层的了解是每一位Java程序员深入Java技术领域的重要因素。本课程试图通过简单易懂的方式,系统...

    深入理解JVM内存结构及运行原理全套视频加资料.txt

    2019最新深入理解JVM内存结构及运行原理(JVM调优)高级核心课程视频教程下载。JVM是Java知识体系中的重要部分,对JVM底层的了解是每一位Java程序员深入Java技术领域的重要因素。本课程试图通过简单易懂的方式,系统...

    JVM自动内存管理机制

    Java自动内存管理机制包含两部分:内存分配和内存回收,要想理解内存分配和回收的机制,则需要了解下Java内存区域(Java运行时数据区),这篇随笔将按照下面的线索进行逐步解析:1.Java运行时数据区2.对象“已死”的...

    JVM垃圾回收机制与GC性能调优

    JVM的堆是Java对象的活动空间,程序中的类的对象从中分配空间,其存储着正在运行着的应用程序用到的所有对象。这些对象的建立方式就是那些new一类的操作,当对象无用后,是GC来负责这个无用的对象(地球人都知道)。

    Java 虚拟机学习笔记:Java 内存区域,垃圾收集,内存分配与回收策略,JVM 调优,文件结构,类加载机制,Java 程序

    内存分配与回收策略, JVM 调优, 文件结构, 类加载机制, Java 程序 Java是一种面向对象的编程语言,由Sun Microsystems于1995年推出。它是一种跨平台的语言,意味着可以在不同的操作系统上运行。Java具有简单、...

    深入JVM内核 - 原理、诊断与优化

    JVM运行机制简介 堆、栈、方法区等 JVM启动流程 内存模型和volatile实例 解释和编译运行的概念 介绍JVM的内部结构、启动流程以及内存模型。并介绍JVM字节码的执行方式。 第三课 常用JVM参数 堆的分配参数 栈分配及...

    Java进阶教程解密JVM视频教程

    * 在内存结构章节,能够学习掌握 JVM内存溢出现象,堆栈内存结构,利用内存诊断工具排查问题。彻底分析 StringTable的相关知识与性能优化,掌握直接内存分配原理和释放手段。 * 在垃圾回收章节,不仅会介绍垃圾回收...

    JVM-Java虚拟机

    能学到什么:1,JVM底层运行机制和原理;2JVM参数;3,垃圾回收原理;4,垃圾回收器的使用;5,调优实战案例 导语:平时我们所说的JVM广义上指的是一种规范。狭义上的是JDK中的JVM虚拟机。JVM的实现是由各个厂商来做...

    深入JVM内核—原理、诊断与优化

    第二周JVM运行机制简介堆、栈、方法区等JVM启动流程内存模型和volatile实例解释和编译运行的概念介绍JVM的内部结构、启动流程以及内存模型。并介绍JVM字节码的执行方式。第三周常用JVM参数堆的分配参数栈分 资源太大...

    深入理解Java虚拟机视频教程(jvm性能调优+内存模型+虚拟机原理)视频教程

    第46节内存分配-长期存活的对象进入老年代00:03:40分钟 | 第47节内存分配-空间分配担保00:04:54分钟 | 第48节内存分配-逃逸分析与栈上分配00:10:32分钟 | 第49节虚拟机工具介绍00:10:27分钟 | 第50节虚拟机工具-...

    Java知识,JVM面试资料

    自动内存管理:JVM 的垃圾回收机制自动管理内存分配和释放,提供了自动内存管理的功能。这大大简化了Java程序员的工作,减少了手动内存管理的错误和风险。同时,它还通过垃圾回收机制实现了自动资源释放,提高了程序...

    面向大数据处理框架的JVM优化技术综述

    当前,以Hadoop、Spark为...这些大数据处理框架采用分布式架构,使用Java、Scala等面向对象语言编写,在集群节点上以Java虚拟机(JVM)为运行时环境执行计算任务,因此依赖JVM的自动内存管理机制来分配和回收数据对象.

    JVM虚拟机,经典java虚拟机

    JVM虚拟机,经典java虚拟机,了解java的运行机制及详细内存分配和垃圾回收

    resin-jvm 调优

    JRockit还提供了更细致的功能用以观察JVM的运行状态,主要是独立的GUI控制台(只能适用于使用Jrockit才能使用jrockit81sp1_141_03自带的console监控一些cpu及memory参数)或者WebLogic Server控制台。 Bea JRockit ...

    深入理解_Java_虚拟机 JVM_高级特性与最佳实践

    / 112 5.2.5 服务器JVM进程崩溃 / 113 5.3 实战:Eclipse运行速度调优 / 114 5.3.1 调优前的程序运行状态 / 114 5.3.2 升级JDK 1.6的性能变化及兼容问题 / 117 5.3.3 编译时间和类加载时间的优化 / 122 5.3.4 ...

    eclipse.ini

    参数中-vmargs的意思是设置JVM参数,所以后面的其实都是JVM的参数了,我们首先了解一下JVM内存管理的机制,然后再解释每个参数代表的含义。 堆(Heap)和非堆(Non-heap)内存 按照官方的说法:java 虚拟机具有一个堆,...

    java虚拟机2021面试题第二季

    2. 内存管理:JVM负责内存的分配和垃圾回收。它会自动为Java应用程序分配内存,并在对象不再被引用时自动释放内存。 3. 字节码验证和安全性检查:JVM对字节码进行验证,以确保它符合Java语言规范,并且没有潜在的...

    java虚拟机2021面试题第三季

    2. 内存管理:JVM负责内存的分配和垃圾回收。它会自动为Java应用程序分配内存,并在对象不再被引用时自动释放内存。 3. 字节码验证和安全性检查:JVM对字节码进行验证,以确保它符合Java语言规范,并且没有潜在的...

    java虚拟机2021面试题第一季

    2. 内存管理:JVM负责内存的分配和垃圾回收。它会自动为Java应用程序分配内存,并在对象不再被引用时自动释放内存。 3. 字节码验证和安全性检查:JVM对字节码进行验证,以确保它符合Java语言规范,并且没有潜在的...

Global site tag (gtag.js) - Google Analytics