您的位置:首页 > 编程语言 > Java开发

JVM学习笔记一:Java内存区域

2015-07-18 10:40 645 查看
在虚拟机自动内存管理机制的帮助下,java程序员不需要去关心每一个对象的内存回收,不容易出现内存泄露和内存溢出问题,由虚拟机管理内存,看起来不需要程序员知道jvm是如何分配和回收内存的。但是不明白jvm底层的运行机理,对于java程序的优化、以及jvm的一些错误改正就无从谈起。


1. 运行时数据区域

JVM所管理下的内存将会包括一下几个运行时数据区域:

程序计数器

虚拟机栈

本地方法栈

方法区





程序计数器

程序计数器是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。当线程执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令地址;如果是Native方法,则为空(undefined)程序计数器为“线程私有”。

虚拟机栈

JVM栈也是线程私有的,他的生命周期与线程相同。JVM栈描述的是Java方法执行的内存模型:每个方法执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出入口等。每一个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。

此区域有StackOverflowError和OutOfMemoryError异常

局部变量表存放编译期可知的各种基本类型(int、double……)、对象引用(reference)和returnAddress类型(指向一条字节码指令的地址)。64位,long和double会占用2个局部变量空间(SLot),其余都为一个。局部变量表所需内存空间在编译器完成分配。

本地方法栈

与虚拟机栈作用类似,但本地方发展为Native方法服务。此区域有StackOverflowError和OutOfMemoryError异常

Java堆

全线程共享的一块区域,用于存放对象实例以及数组。是垃圾回收器主要管理区域。从内存分配的角度看,java堆中可能划分出多个线程私有的分配缓冲区(TLAB)。

会有OutOfMemoryError异常

方法区

用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,线程共享。该区域的内存回收主要是针对常量池的回收和对类型的卸载。

2.对象

对象的创建

检查这个指令的参数是否能在常量池中定位到一个类的符号引用

检查这个符号引用代表的类是否已被加载、解析和初始化

类加载检查通过后,为新生对象分配内存

对象的内存分配


1.指针碰撞

假设Java堆中内存是绝对规整的,所有用的内存都放在一边,空闲放在另一边。中间放着一个指针作为分界点的指示器,分配内存仅仅是将指针向空闲一侧移动。(Serial、ParNew)

2.空闲列表

Java内存不是规整的,已使用和未使用交错,虚拟机必须维护一个列表,记录内存使用情况,再分配时在列表中找到一块足够大的内存划分个对象。

对象的内存布局

对象在内存中的布局分为3块区域:

对象头(Header)、实例数据(Instance Data)和对象填充(Padding)。

对象头:包括两部分信息,第一部分用于存储对象自身的运行时数据(如哈希码、GC分代年龄、锁状态标志、线程持有锁……),这部分数据的长度在32位和64位虚拟机中分别为32bit和64bit,官方成为“Mark Word”。另一部分是类型指针,即对象指向它的类元数据的指针。虚拟机通过这个指针确定这个对象是哪个类的实例。(如果是数组,则对象头还记录数组长度的数据)

实例数据是对象真正存储的有效信息.

对齐填充仅仅占位符的作用

对象访问定位

1.句柄

2.直接指针

句柄

java堆中划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址。而句柄中包含了对象实例数据与类型数据各自的具体地址信息。

直接指针

reference中存储的直接就是对象地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jvm