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)、对象的引用(不是对象本身,仅仅是引用指针)、方法返回地址等。
虚拟栈中规定了两种异常状况:
- 如果线程请求的深度大于虚拟机所允许的深度,就会抛出stackoverflowerror异常,也就是栈溢出异常。在使用递归的调用方法的情况下,很容易抛出这个异常。
- 如果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件事情:
- 哪些内存需要回收
- 什么时候需要回收
- 怎么样回收
方法区的回收:
方法区即后文提到的永久代,很多人认为永久代是没有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都有大批对象死去,只有少量存活,那就选用复制算法只需要付出少量存活对象的复制成本就可以完成收集。
相关推荐
基于springboot大学生就业信息管理系统源码数据库文档.zip
基于java的驾校收支管理可视化平台的开题报告
时间序列 原木 间隔5秒钟 20241120
毕业设计&课设_基于 Vue 的电影在线预订与管理系统:后台 Java(SSM)代码,为毕业设计项目.zip
基于springboot课件通中小学教学课件共享平台源码数据库文档.zip
基于java的网上购物商城的开题报告
Delphi人脸检测与识别Demo1fdef-main.zip
基于java的咖啡在线销售系统的开题报告
基于java的自助医疗服务系统的开题报告.docx
内容概要:本文档全面介绍了Visual Basic(VB)编程语言的基础知识和高级应用。首先概述了VB的基本特性和开发环境,随后详细讲述了VB的数据类型、变量、运算符、控制结构、数组、过程与函数、变量作用域等内容。接着介绍了窗体设计、控件使用、菜单与工具栏的设计,文件操作、数据库访问等关键知识点。最后讨论了VB的学习方法、发展历史及其在桌面应用、Web应用、数据库应用、游戏开发和自动化脚本编写等领域的广泛应用前景。 适合人群:初学者和中级程序员,尤其是希望快速掌握Windows桌面应用开发的人群。 使用场景及目标:①掌握VB的基础语法和开发环境;②学会使用VB创建复杂的用户界面和功能完整的应用程序;③理解数据库操作、文件管理和网络编程等高级主题。 其他说明:Visual Basic是一种简单易学且功能强大的编程语言,尤其适合用于开发Windows桌面应用。文中不仅覆盖了基础知识,还包括了大量的实用案例和技术细节,帮助读者快速提升编程技能。
基于java的疫情期间高校防控系统开题报告.docx
基于springboot+vue社区老年人帮扶系统源码数据库文档.zip
基于java的超市商品管理系统的开题报告.docx
基于SpringBoot房屋买卖平台源码数据库文档.zip
xdu限通院23微处理器系统与应用大作业(两只老虎),适应于汇编语言keil软件,
<项目介绍> - 新闻类网站系统,基于SSM(Spring、Spring MVC、MyBatis)+MySQL开发,高分成品毕业设计,附带往届论文 - 不懂运行,下载完可以私聊问,可远程教学 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 --------
基于java的学生网上请假系统的开题报告.docx
社会经济繁荣发展的今天,电子商务得到了飞速发展,网上交易越来越彰显出其独特的优越性,在人们的日常生活中,出现了各种类型的交易网站。其中一个就是车辆易主交易网站,它是一个服务于用户买卖二手车辆的交易网站,为用户提供了平等互利、方便快捷的网上交易平台,通过这一类型的网站,用户可自由出售和购买车辆。 本课题主要根据车辆本身的特性,充分发挥互联网的特点与优势,构建一个以二手车辆为商品、基于互联网平台的车辆易主业务交易管理系统,并根据车辆易主业务交易管理系统的应用需求,进行需求分析,进而对网站系统作规划设计。采用IDEA为运行平台,以SSH为框架,运用HTML语言、JSP技术、MySql数据库、JSP与后台数据库链接等关键技术建设二手车网上交易系统,构建车辆易主交易系统的会员注册与登录,网站首页展示、用户发布商品车辆,用户求购商品车辆,分页浏览、购物系统、用户后台管理、管理员用户后台管理等功能,并使这些功能得以实现并更好为用户服务。网站整体构建完成且测试成功后,用户可以进入网站进行注册、登录,登录后,用户可以在网站上发布自己的闲置车辆或者寻找想要购买的车辆,还可以收藏车辆,管理发布和收藏的车辆,
SQLite3的向量扩展库,windows dll,版本0.1.5
基于C++实现(控制台)商品库存管理系统