【《深入理解java虚拟机》-整理笔记】 by  lijun

JVM虚拟机内存逻辑模型

  • 方法区(全局变量 静态数据 常量等) 线程共享
  • 堆栈区(对象实例 数组数据 new generation old generation对象等) 线程共享
  • 本地方法栈
  • 方法栈(局部变量 操作数 对象引用) 线程私有
  • 程序计数器 

程序计数器(Program Counter Register)

类似于PC寄存器,是一块较小的内存区域,通过程序计数器中的值寻找要执行的指令的字节码,由于多线程间切换时要恢复每一个线程的当前执行位置,所以每个线程都有自己的程序计算器。这一个区域不会有OutOfMemeryError。当执行Java方法时,这里存储的执行的指令的地址,如果执行的是本地方法,这里的值是Undefined。

虚拟机栈(Java Stack)

虚拟机栈也是线程私有的,每创建一个线程,虚拟机就会为这个线程创建一个虚拟机栈,虚拟机栈表示Java方法执行的内存模型,每调用一个方法,就会生成一个栈帧(Stack Frame)用于存储方法的本地变量表、操作栈、方法出口等信息,当这个方法执行完后,就会弹出相应的栈帧。

如果请求的栈的深度过大,虚拟机可能会抛出StackOverflowError异常,如果虚拟机的实现中允许虚拟机栈动态扩展,当内存不足以扩展栈的时候,会抛出OutOfMemoryError异常。

栈帧(Stack Frame)

栈帧分为三部分:局部变量区(Local Variables)、操作数栈(Operand Stack)和帧数据区(Frame Data)。

局部变量区(Loca Variables)

局部变量区被组织一个一个从0开始的字数组,byte、short、char在存储前被转换为int,boolean也被转换为int,0表示false,非0表示true,long和double占据两个字长。

操作数栈(Operand Stack)

操作数栈也被组织为一个字数组,但不同于局部变量区,它不是通过数组下标访问的,而是能过栈的Push和Pop操作,前一个操作Push进的数据可以被下一个操作Pop出来使用。

帧数据区(Frame Data)

这部分的作用主要有三部分:

  • 常量池中数据的解析
  • 方法执行完后处理方法返回,恢复调用方现场
  • 方法执行过程中抛出异常时的异常处理,存储有一个异常表,当出现异常时虚拟机查找相应的异常表看是否有对应的Catch语句,如果没有就抛出异常终止这个方法调用

本地方法栈(Native Method Stack)

与虚拟机栈类似,只是是执行本地方法时使用的。

方法区(Method Area)

用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译后的代码等信息。

方法区是线程间共享的,当两个线程同时需要加载一个类型时,只有一个类会请求ClassLoader加载,另一个线程会等待。

对于每一个加载的类型,会在方法区中保存以下信息:

  • 类及其父类的全限定名(java.lang.Object没有父类)
  • 类的类型(Class or Interface)
  • 访问修饰符(public, abstract, final)
  • 实现的接口的全限定名的列表
  • 常量池
  • 字段信息
  • 方法信息
  • 除常量外的静态变量
  • ClassLoader引用
  • Class引用

对于每一个字段,会在方法区中保存以下信息(字段声明顺序也会保存):

  • 字段名
  • 字段的类型
  • 字段的修饰符(public, private , protected, static, final, volatile, transient)

对于每一个方法,会在方法区中保存以下信息(方法声明顺序也会保存):

  • 方法名
  • 方法返回类型(或void)
  • 参数信息
  • 方法修饰符(public, private, protected , static, final, synchronized, native, abstract)

如果方法不是抽象方法并不是本地方法(Native Method),还会保存以下信息:

  • 方法的字节码
  • 本地变量表及操作数栈的大小
  • 异常表

虚拟机需要存储一些数据,用来快速地访问一个类对象中的方法,一般实现为一个方法表。

方法区中还有一部分是运行时常量池,主要用来存储编译时生成的字面量和符号引用,常量也可以在运行时产生,如String的intern方法。

方法区中也可能存在GC,但虚拟机规范对此不做要求,主要是回收一些常量和卸载一些不用的类型信息,不过要卸载一个类的条件很难达到,而且些处GC其实也回收不了多少内存。

堆(Heap)

虚拟机中用于存放对象与数组实例的地方,垃圾回收的主要区域就是这里(还可能有方法区)。

如果垃圾收集算法采用按代收集(目前大都是这样),这部分还可以细分为新生代和老年代。

新生代又可能分为Eden区,From Survivor区和To Survivor区,主要是为了垃圾回收。所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)。

Java堆只要求逻辑上是连续的,在物理空间上可以不连续。

直接内存

JDK1.4中引用了NIO,并引用了Channel与Buffer,可以使用Native函数库直接分配堆外内存,并通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。

对象访问

当新建一个对象时,会在堆中为这个对象分配内存,并在栈中有一个对这个对象引用,除此之外,在Java堆中还要能通过这个对象找到它的类型信息(对象类型,父类,实现的接口,包含的字段与方法等)。

Reference在Java虚拟机中定义为指向对象的引用,但没有定义这个Reference应该有怎么实现。

一种实现是Reference直接存储对象在堆内的地址,对象的类型信息可以在对象在堆中的内存布局中存储,如存储在对象内存的开头等。

另一种实现是Reference指向一个句柄表中的一个位置,句柄中保存了对象的实际位置及它对应的类型信息。

使用句柄的好处是当在内存中移动对象的位置时,只需要更新句柄表中的内容,不需要改变引用值,但会多一次内存访问开销,直接引用的优缺点与此相反。

参考资料:

    1. 深入Java虚拟机
    2. 深入理解Java虚拟机-JVM高级特性与最佳实践
    3. 浅析java虚拟机
    4. 探秘java虚拟机--内存管理i

JVM 虚拟机内存深入探究的更多相关文章

  1. 设置TOMCAT的JVM虚拟机内存大小

    你知道如何设置TOMCAT的JVM虚拟机内存大小吗,这里和大家分享一下,JAVA程序启动时JVM都会分配一个初始内存和最大内存给这个应用程序.这个初始内存和最大内存在一定程度都会影响程序的性能. 设置 ...

  2. java中JVM虚拟机内存模型详细说明

    java中JVM虚拟机内存模型详细说明 2012-12-12 18:36:03|  分类: JAVA |  标签:java  jvm  堆内存  虚拟机  |举报|字号 订阅     JVM的内部结构 ...

  3. jvm 虚拟机内存模型

    来源:https://blog.csdn.net/A_zhenzhen/article/details/77917991?locationNum=8&fps=1    https://blog ...

  4. JVM虚拟机 - 内存

    在JVM虚拟机中,内存部分大致可以分为以下几类: Heap:堆 NonHeap:非堆 CodeCache:缓存编辑后的机器码的内存区域 CompressedClassSpace:类压缩空间 MetaS ...

  5. 面试官:别的我不管,这个JVM虚拟机内存模型你必须知道

    前言 说jvm的内存模型前先了解一下物理计算机的内存处理. 物理计算器上用户磁盘和cpu的交互,由于cpu读写速度速度远远大于磁盘的读写速度速度,所以有了内存(高速缓存区).但是随着cpu的发展,内存 ...

  6. JVM 虚拟机 内存说明

    Java虚拟机所管理的内存图: 1程序计数器: 该模块作用是记录线程执行字节码的位置,记录程序执行状态,以便该线程下次获得CPU时继续执行.程序里的每个线程程序计数器相互独立,互不影响,该内存区也称“ ...

  7. JVM虚拟机内存模型以及GC机制

    JAVA堆的描述如下: 内存由 Perm 和 Heap 组成. 其中 Heap = {Old + NEW = { Eden , from, to } } JVM内存模型中分两大块,一块是 NEW Ge ...

  8. JVM虚拟机内存溢出垃圾收集及类加载机制总结

    1.Java内存区域与内存溢出异常 虚拟机栈:为虚拟机执行Java方法服务 本地方法栈:为虚拟机使用到的native方法服务. Java堆:是Java虚拟机所管理的内存中最大的一块,被所有线程共享的一 ...

  9. 对jvm虚拟机 内存溢出的思考

    java内存溢出:当新产生对象时,新生代空间不够,导致无法申请到足够的空间,报内存溢出 内存泄漏:一些静态集合,静态常量没有被gc清理,越来越多,占用内存,最后导致无法申请到新的空间

随机推荐

  1. IOS下去掉input submit圆角和

    在iOS系统下input submit会有圆角,如果添加有背景色,背景色出错,在安卓系统是没有这些问题,可以在input样式加上这段样式 input{ -webkit-appearance: none ...

  2. moment.js(moment-in-node.js)获取本月最后一天 不指定

    http://tommyhu.cn/moment-in-nodejs/ //获取本月最后一天 to=using.moment(日期).endOf('month').format("YYYY- ...

  3. log4j 2整理

    # Log4j 2最佳实践 #Log4j的1.x版本已经被广泛使用于很多应用程序中.然而,它这些年的发展已经放缓.它变得越来越难以维护,因为它需要严格遵循很老的Java版本,并在2015年8月寿终正寝 ...

  4. JS常用的设计模式(4)——适配器模式

    去年年前当时正在开发dev.qplus.com, 有个存储应用分类id的js文件, 分类id的结构最开始设计的比较笨重. 于是我决定重构它. 我把它定义成一个json树的形式, 大概是这样: var ...

  5. SQL中CASE 的用法 转载

    sql语言中有没有类似C语言中的switch case的语句?? 没有,用case   when   来代替就行了.              例如,下面的语句显示中文年月 select getdat ...

  6. 1、块:ion-item

    因为ion-item 一般写于ion-list里 所以在ion-list里面我会仔细讲解. 1. ion-badge /* --- page1.html ---*/ <ion-navbar *n ...

  7. tomcat一个IP绑定多个域名,不同域名访问不同的应用

    修改conf文件夹下面的server.xml的Engine里面的内容即可原始内容如下: …… <Engine name="Catalina" defaultHost=&quo ...

  8. python之高阶函数filter

    原文 Python内建的filter()函数用于过滤序列. 和map()类似,filter()也接收一个函数和一个序列.和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返 ...

  9. python 对象属性与 getattr & setattr

    Python对象的属性可以通过obj.__dict__获得,向其中添加删除元素就可以实现python对象属性的动态添加删除的效果,不过我们应该使用更加正规的getattr和setattr来进行这类操作 ...

  10. OLEDB 数据变更通知

    除了之前介绍的接口,OLEDB还定义了其他一些支持回调的接口,可以异步操作OLEDB对象或者得到一些重要的事件通知,从而使应用程序有机会进行一些必要的处理.其中较有用的就是结果集对象的变更通知接口.通 ...