本文主要介绍Java虚拟机的内存分布以及对象的创建过程。

一、Java虚拟机的内存分布

文章开始前读者需要了解Java虚拟机的运行时数据区是怎样划分的。如下图所示:

1、程序计数器(Program Counter Register)

  程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

  由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,所以在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能够恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器。

  各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

  熟悉C++的读者应该了解“现场保护”这个概念,虽然“现场保护”对应的是函数调用,而这里的程序计数器对应的是线程之间的切换,但笔者认为它们的作用有点相似:都是为了线程或函数执行完后能够恢复到正确的位置。

2、Java虚拟机栈(Java Virtual Machine Stacks)

  我们平时所说的栈区就是Java虚拟机栈。

  与程序计数器一样,Java虚拟机栈也是线程私有的,生命周期与线程相同。

  虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈桢(Stack Frame)用于存储该方法的信息,如局部变量表、操作数栈、方法的出口等信息。

  每个方法从调用直至执行完成的过程,就对应着一个栈桢入栈到出栈的过程。

  局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,不是对象本身)和returnAddress类型(指向了一条字节码指令的位置)。

  Java虚拟机规范中,对这个区域规定了两种异常状况:StackOverflowError异常和OutOfMemoryError异常。

3、Java堆(Java Heap)

  Java堆是Java虚拟机所管理的内存中最大的一块,该内存是被所有线程共享的,在虚拟机启动时创建。此内存区域存在的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。既然Java堆中存放了几乎所有的实例,那么这里自然就成了垃圾收集器管理的主要区域。

  当堆中没有内存完成了实例分配,并且堆也无法再扩张时,将会抛出OutOfMemoryError异常。

4、方法区(Method Area)

  方法区与Java堆一样,是各个线程共享的内存区域。

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

  方法区还包括了运行时常量池。

  当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

5、本地方法栈(Native Method Stack)

  本地方法栈与虚拟机栈的作用非常相似,它们的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为了虚拟机使用到的Native方法服务。

  在虚拟机规范中队本地方法栈方法使用的语言、使用的算法与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(如 Sun HotSpot 虚拟机)直接就把本地方法栈和虚拟机栈合二为一。

  该数据区抛出的异常和虚拟机栈相同:都会抛出StackOverflowError和OutOfMemoryError异常。

6、运行时常量池(Runtime Constant Pool)

  运行时常量池属于方法区的一部分。虽然Java虚拟机没有单独为运行时常量池划分内存空间,但是由于该区域在平时开发中也是一个重要的部分,所以也为它单独列一个条目。

  Class文件中除了有类的版本、字段、方法、接口的描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量的引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

  运行时常量池相对于Class文件常量池的另外一个重要特征是具有动态性,Java语言并不要求常量一定只有编译期才能产生,也就是说并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,如String类的intern()方法。

  既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存是会抛出OutOfMemoryError异常。

二、对象的创建过程

  Java是一门面向对象的语言,在Java程序运行过程中无时无刻都会有对象被创建出来。在语言层面上通产仅仅是一个new关键字,而在虚拟机中可就没有这么简单了:

  虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。  在类加载检查通过后,接下来虚拟机将为新生对象在Java堆中分配内存(对象所需的内存大小在内加载完成后便可完全确定)。接下来,虚拟机要对对象进行一些必要的设置,如这个对象是哪个类的实例、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Header)之中。

  在上面工作都完成之后,从虚拟机的视角来看:一个新的对象已经产生了,但从Java程序的视角来看:对象创建才刚刚开始——<init>方法还没有执行,所有的字段都还为零。一般来说执行new指令之后会接着执行<init>方法,把对象按照程序员的意愿进行初始化。这样一个真正可用的对象才算完全产生出来。

本文章系笔者的第一篇文章,文章中难免有错误和不足的地方,还请大家指正。

Java虚拟机 - 内存模型的更多相关文章

  1. Java虚拟机内存模型及垃圾回收监控调优

    Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存 ...

  2. Java虚拟机--内存模型与线程

    Java虚拟机--内存模型与线程 高速缓存:处理器要与内存交互,如读取.存储运算结果,而计算机的存储设备和处理器的运算速度差异巨大,所以加入一层读写速度和处理器接近的高速缓存来作为内存和处理器之间的缓 ...

  3. JAVA虚拟机内存模型

    一.对于Java程序员来说,在虚拟机的自动内存管理机制下,我们不需要为每一个new操作去写匹配的delete/free操作 但是当我们对于内存的管理了解有能够帮助我们理解Java虚拟机的垃圾回收机制. ...

  4. 深入理解Java虚拟机内存模型

    前言 本文中部分内容引用至<深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)>第12章,如果有兴趣可自行深入阅读,文末放有书籍PDF版本连接. 一.物理机中的并发 物理机遇到的并 ...

  5. 面试常问的Java虚拟机内存模型,看这篇就够了!

    一.虚拟机 同样的java代码在不同平台生成的机器码肯定是不一样的,因为不同的操作系统底层的硬件指令集是不同的. 同一个java代码在windows上生成的机器码可能是0101.......,在lin ...

  6. java 虚拟机内存模型

    [声明] 欢迎转载,但请保留文章原始出处→_→ 文章来源:[http://www.cnblogs.com/smyhvae/p/4748392.html] 文章来源:[http://www.cnblog ...

  7. Java虚拟机03(Java虚拟机内存模型)

    根据 JVM 规范,JVM 内存共分为虚拟机栈.堆.方法区.程序计数器.本地方法栈五个部分. 其实最需要Java程序员关注的是堆,栈,还有方法区,因为啊: 如果代码又问题的话,可能回出现栈溢出 然后说 ...

  8. 作业1:java虚拟机内存模型图示

    看了很多篇文章,整理成一幅图,但仍然有许多不解的地方,以后再接着完善,哪位大神看到不正确的地方,请指出,谢谢.

  9. 栈帧 2.6. Frames 虚拟机内存模型

    https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-2.html#jvms-2.6 小结: 1. https://docs.oracle. ...

随机推荐

  1. Oracle数据库,查询语句、内置函数

    一.数据库的查询语句: 1.查询整个表: select * from 表名 例: 2.通过条件查询某一行数据: select * from 表名 where 字段名 例: 3.某一列数据去重查询: s ...

  2. x8086汇编实现dos清屏(clear screen)

    题目要求:x8086汇编实现dos下的清屏功能 80X25彩色字符模式显示缓冲区的结构: 在内存地址结构中,B8000H~BFFFFH共32KB的空间,为80x25彩色字符模式的显示缓冲区.向这个地址 ...

  3. 泛函编程(28)-粗俗浅解:Functor, Applicative, Monad

    经过了一段时间的泛函编程讨论,始终没能实实在在的明确到底泛函编程有什么区别和特点:我是指在现实编程的情况下所谓的泛函编程到底如何特别.我们已经习惯了传统的行令式编程(imperative progra ...

  4. 浅谈spring security 403机制

    403就是access denied ,就是请求拒绝,因为权限不足 三种权限级别 一.无权限访问 <security:http security="none" pattern ...

  5. 简单可用好实现的 HA 高可用设计

    本文为作者原创,如需转载请注明出处. 1. 实现的功能 一主多备,自动选主 启动记录可查询 2. 前置需求 一台数据库用以记录,如 MySQL.Redis.MongoDB 等.关键是设计中的思想,用啥 ...

  6. 一个ORM的实现(附源代码)

    1 前言 经过一段时间的编写,终于有出来一个稳定的版本,期间考虑了多种解决方案也偷偷学了下园子里面大神们的作品. 已经有很多的ORM框架,为什么要自己实现一个?我的原因是在遇到特殊需求时,可以在ORM ...

  7. 【追寻javascript高手之路01】javascript参数知多少?

    前言 我最近在思考一个问题,我本身平时还是积累了不少东西,面试时候问的东西基本逃不出写的博客(当然,高级阶段的就不行了),但是真的被问到时我却不一定答得上来. 知道且能回答,回答的效果都不是很好... ...

  8. web前端命名规范

    在做web项目的时候,命名的规范是很重要.初学者一般急于求成对命名规范没有概念,觉得花时间这些还不如多看几遍框架.其实在我看来,一个良好的命名习惯是很重要的.下面就来介绍一下我总结的命名规范: (1) ...

  9. 如何在启用SharePoint浏览器功能的InfoPath 表单中添加托管代码以动态地加载并显示图片

    InfoPath 的浏览器表单不支持加载并显示图片,当然在模板中可以插入图片,但是如果想显示数据库的一幅图片,或是动态加载一张图片就无能为力了. 基实这个问题可以通过在浏览器表单中使用: " ...

  10. Httplistener Access Denied

    HttpListener.Start() 会出现HttpListenerException, 显示拒绝访问 一般是因为有些计算机账户是没有权限创建 HttpListener服务, 但是可以注册一些规则 ...