深入学习java虚拟机(一)
2016-04-14 21:01
323 查看
运行时数据区域
Java虚拟机在执行Java程序过程中把所管理的内存划分为以下几个不容的数据区域,这些区域都有各自的用途,以及创建时间和销毁时间。
程序计数器、虚拟机栈、堆、方法区、本地方法栈
程序计数器
[b]背景:java 多线程是通过线程轮流切换并分配处理执行时间来实现,在任何一个时刻,一个处理器只会执行一条线程的指令。因此需要用一块内存空间记录线程的执行状态,这就是程序计数器。[/b]
存储的内容:存放程序下一条指令的地址地方。
目的:为了线程切换后能够恢复到正确的执行位置,每条线程都有一个独立的程序计数器。(唯一一个没有没有OutOfMemeryEror情况的区域)。“记录现场,恢复现场”
java虚拟机栈
存放内容:局部变量表、操作数栈、动态链接、方法出口(线程私有,生命周期与线程相同。)
虚拟机栈:是Java方法执行的内存模型。每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口。
生命周期:每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中的的入栈到出栈过程
局部变量表存放了编译期可知的各种基本数据类型、对象引用和returnAdderss类型
当进入一个方法时,这个方法所需要在帧中分配多大的局部变量空间是完全确定的。方法运行期间不会改变局部变量表的大小
Java虚拟机规范中对这个区域定义了两种异常状况:StackOverflowError(线程请求栈深度 > 虚拟机所允许的深度)。OutOfMemoryError(如果Java虚拟机动态扩展无法申请足够内存的时候)
本地方法栈
与虚拟机栈一致,唯一区别虚拟机栈为 java 方法服务。本地方法栈是为 native 方法服务。
Java 堆
目的:唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配。
Java 堆是Java虚拟机所管理的内存中最大的一块。
Java堆是被所有线程共享的一块内存区域,在虚拟机启动的时候创建。
Java 堆是垃圾回收器管理的主要区域,因此也被称为“GC堆”(垃圾堆)。
分类:由于现在垃圾收集器基本采用分代收集算法,所以Java堆中还可以分为:新生代,老年代;再细致一点还有 Eden空间、From Survivor 空间、 To Survivor空间。
根据Java虚拟机规范,Java堆可以处于物理上不连续的内存空间中,只要逻辑连续即可
如果堆中没有内存完成分配实例,并且堆也无法扩展,将会抛出OutOfMemory异常
方法区
存储对象:它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码。方法区也被称为永久带
与Java堆一样,是各个线程的共享的内存区域。
深入探讨Hotspot虚拟机在Java堆中的对象分布,布局和访问的过程
对象的创建
虚拟机遇到 new 指令时候,首先检查指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已经被加载、解析、初始化。如果没有,那必须执行相应的类加载过程
在类加载通过后,虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后便可以完全确定。
Java 堆为对象分类内存的两种方式
指针碰撞:假设java堆是绝对规整的,所有用过的内存放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,分配内存就仅仅是把指针向空闲空间那边挪动一段与对象大小相等的距离
空闲列表:如果Java 堆并不是规整的,已使用的内存和空闲内存相互交错,虚拟机就必须维护一个列表,记录哪些内存块是可用的。
选择哪种方式取决于Java堆是否规整,而Java堆是否规整又取决于垃圾收集器是否带有压缩整理功能决定。
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值
虚拟机对对象进行必要设置,例如找个对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码、对象的GC分代年龄。这些信息都存放在对象的对象头中。
从虚拟机视角来看,一个新的对象已经产生,但从Java程序视角来看,对象创建才刚刚开始--<init>方法还没有执行。所有字段都还是零。执行new指令之后会接着执行<init>方法,把对象按照程序员的意愿进行初始化。这样一个真正的对象才算完全产生。
总结:JVM遇到new指令 —> 检查指令参数,与常量池中类符号引用匹配 & 检查类是否被加载 —> Java 堆为对象分配内存(分配内存两种方式)—> 内存空间初始化为零 & 对对象进行必要设置—> 执行init方法
对象的内存分布
对象在内存中存储的布局可以分为3个区域:对象头,实例数据,对齐填充
对象头:包含用于存储对象自身的运行时数据,类型指针
自身运行时数据:HashCode 哈希码、GC分代年龄、锁状态标识、线程持有的锁、偏向线程ID、偏向时间戳。
类型指针:对象指向它的类元数据的指针。如果对象是一个Java数组,那么对象头中还必须存储数组长度。
实例数据:对象真正存储的有效信息,也是程序代码中所定义的各种类型的字段内容。
对齐填充:仅仅起占位符的作用。HotSpot VM 的自动内存管理系统的要求对象起始地址必须是8的整数倍,也就是说对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的整数倍。所以对象实例部分没有对齐时,就需要通过对齐填充来补全。
对象的定位访问
产生背景:Java 程序需要通过栈上的reference数据来操作堆上的具体对象。由于reference类型在Java虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用该通过何种方式去定位、访问堆中的对象的具体位置。
定位方式:句柄、直接指针。
句柄:Java堆中将会划分出一块内存作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。
Java虚拟机在执行Java程序过程中把所管理的内存划分为以下几个不容的数据区域,这些区域都有各自的用途,以及创建时间和销毁时间。
程序计数器、虚拟机栈、堆、方法区、本地方法栈
程序计数器
[b]背景:java 多线程是通过线程轮流切换并分配处理执行时间来实现,在任何一个时刻,一个处理器只会执行一条线程的指令。因此需要用一块内存空间记录线程的执行状态,这就是程序计数器。[/b]
存储的内容:存放程序下一条指令的地址地方。
目的:为了线程切换后能够恢复到正确的执行位置,每条线程都有一个独立的程序计数器。(唯一一个没有没有OutOfMemeryEror情况的区域)。“记录现场,恢复现场”
java虚拟机栈
存放内容:局部变量表、操作数栈、动态链接、方法出口(线程私有,生命周期与线程相同。)
虚拟机栈:是Java方法执行的内存模型。每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口。
生命周期:每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中的的入栈到出栈过程
局部变量表存放了编译期可知的各种基本数据类型、对象引用和returnAdderss类型
当进入一个方法时,这个方法所需要在帧中分配多大的局部变量空间是完全确定的。方法运行期间不会改变局部变量表的大小
Java虚拟机规范中对这个区域定义了两种异常状况:StackOverflowError(线程请求栈深度 > 虚拟机所允许的深度)。OutOfMemoryError(如果Java虚拟机动态扩展无法申请足够内存的时候)
本地方法栈
与虚拟机栈一致,唯一区别虚拟机栈为 java 方法服务。本地方法栈是为 native 方法服务。
Java 堆
目的:唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配。
Java 堆是Java虚拟机所管理的内存中最大的一块。
Java堆是被所有线程共享的一块内存区域,在虚拟机启动的时候创建。
Java 堆是垃圾回收器管理的主要区域,因此也被称为“GC堆”(垃圾堆)。
分类:由于现在垃圾收集器基本采用分代收集算法,所以Java堆中还可以分为:新生代,老年代;再细致一点还有 Eden空间、From Survivor 空间、 To Survivor空间。
根据Java虚拟机规范,Java堆可以处于物理上不连续的内存空间中,只要逻辑连续即可
如果堆中没有内存完成分配实例,并且堆也无法扩展,将会抛出OutOfMemory异常
方法区
存储对象:它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码。方法区也被称为永久带
与Java堆一样,是各个线程的共享的内存区域。
深入探讨Hotspot虚拟机在Java堆中的对象分布,布局和访问的过程
对象的创建
虚拟机遇到 new 指令时候,首先检查指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已经被加载、解析、初始化。如果没有,那必须执行相应的类加载过程
在类加载通过后,虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后便可以完全确定。
Java 堆为对象分类内存的两种方式
指针碰撞:假设java堆是绝对规整的,所有用过的内存放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,分配内存就仅仅是把指针向空闲空间那边挪动一段与对象大小相等的距离
空闲列表:如果Java 堆并不是规整的,已使用的内存和空闲内存相互交错,虚拟机就必须维护一个列表,记录哪些内存块是可用的。
选择哪种方式取决于Java堆是否规整,而Java堆是否规整又取决于垃圾收集器是否带有压缩整理功能决定。
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值
虚拟机对对象进行必要设置,例如找个对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码、对象的GC分代年龄。这些信息都存放在对象的对象头中。
从虚拟机视角来看,一个新的对象已经产生,但从Java程序视角来看,对象创建才刚刚开始--<init>方法还没有执行。所有字段都还是零。执行new指令之后会接着执行<init>方法,把对象按照程序员的意愿进行初始化。这样一个真正的对象才算完全产生。
总结:JVM遇到new指令 —> 检查指令参数,与常量池中类符号引用匹配 & 检查类是否被加载 —> Java 堆为对象分配内存(分配内存两种方式)—> 内存空间初始化为零 & 对对象进行必要设置—> 执行init方法
对象的内存分布
对象在内存中存储的布局可以分为3个区域:对象头,实例数据,对齐填充
对象头:包含用于存储对象自身的运行时数据,类型指针
自身运行时数据:HashCode 哈希码、GC分代年龄、锁状态标识、线程持有的锁、偏向线程ID、偏向时间戳。
类型指针:对象指向它的类元数据的指针。如果对象是一个Java数组,那么对象头中还必须存储数组长度。
实例数据:对象真正存储的有效信息,也是程序代码中所定义的各种类型的字段内容。
对齐填充:仅仅起占位符的作用。HotSpot VM 的自动内存管理系统的要求对象起始地址必须是8的整数倍,也就是说对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的整数倍。所以对象实例部分没有对齐时,就需要通过对齐填充来补全。
对象的定位访问
产生背景:Java 程序需要通过栈上的reference数据来操作堆上的具体对象。由于reference类型在Java虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用该通过何种方式去定位、访问堆中的对象的具体位置。
定位方式:句柄、直接指针。
句柄:Java堆中将会划分出一块内存作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。
相关文章推荐
- Java程序生成linechart report的方法
- Java中集合框架的共性功能
- JAVA实验三 抽象类的继承和接口的实现 (多态)
- eclipse快捷键
- Mybatis项目小结
- Java代码执行顺序
- spring,mybatis事务管理配置与@Transactional注解使用
- java学习---java内存泄露有哪些
- Java 中的方法内部类
- 排序算法-归并排序
- [置顶] Java设计模式(一) 简单工厂模式不简单
- Java IO1:IO和File
- Java 中的静态内部类
- spring aop如何织入代码
- Error: could not open `C:\Java\jre7\lib\amd64\jvm.cfg';JAVA_HOME环境变量失效的解决方法
- Spring mvc实现验证码
- 5、java反射基础
- eclipse的jni配置
- struts2整合kindeditor有感
- Spring mvc + MyBatis 整合Shiro