Java的内存管理(一),Java内存管理(
Java的内存管理(一),Java内存管理(
这是学习Java内存模型的第一篇文章,主要参考《深入理解Java虚拟机》、《Think in Java》。本文主要涉及:
1. Java的各个运行时区域以及一些特性
2. 通过一个Java类来明确各个部分在内存中是如何存储的(主要是堆和Java虚拟机)
运行时数据区域
运行时数据区域主要包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区等。
程序记数器
Java程序在虚拟机中会被”翻译”成字节码,而程序计数器则可以被看作当前线程所执行的字节码的行号指示器。如果一个线程在执行一个java的方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果是native方法,那么这个计数器值为空。注意,任何一个确定的时刻,一个处理器都只会执行一条线程,所以这块内存是“线程私有的”。
Java虚拟机栈
Java虚拟机栈是Java方法执行的内存模型,即方法域中的变量存在此,也是线程私有的,它每个方法在执行的时候会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。通俗所说的“栈”内存就是指的这个部分。
局部变量表存放了编译期可知的各种基本数据类型、对象引用(引用的地址是Java堆的某个地址)和returnAddress类型(指向了一条字节码指令的地址)。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量是完全确定的。
Java虚拟机中如果线程请求的栈深度大于了虚拟机所允许的深度,将抛出StackOverflowError异常,如果虚拟机在扩展时无法申请到组都内存,会抛出OutOfMemoryEroor异常。
本地方法栈
本地方法栈与虚拟机方法栈发挥的作用非常相似,区别在于调用方法是Java方法还是本地方法。有的虚拟机(如Sun Hotspot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。
Java堆
Java对是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。此外,堆也是垃圾回收的主要区域。
对象在内存中可分为3块区域:对象头、实例数据和对头填齐。
对象头存储自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。另一部分是类型指针,即指向它的类元数据的指针,部分虚拟机通过这个指针来确定这个对象是哪个类的实例。
实例数据存储对象真正有效的信息,也是在程序代码中所定义的(非静态)成员变量。
对齐填充不是必然存在的,仅仅起着占位作用,保证对象的起始地址是8字节的整数倍。
方法区
方法区也是线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、及时编译器变异后的代码等数据。这部分在类加载中说明。
程序示例
总的来说,程序的运行是一条条执行方法的过程,方法(指令)存在方法区,方法域中的变量存在虚拟机栈中,其中,方法与中的变量有对对象引用的,这时候方法区存的是对象引用,具体信息存在Java堆中。下面的代码段介绍了从入口点开始,一个java程序执行的内存模型以及堆栈情况。
package com.test;
/**
* Created by RuiDu on 2016/11/22.
*/
public class EntryPoint {
//这个方法的入口,会创建一个为main的栈帧
public static void main(String[] args){
Test test = new Test();//会开始进行Test的初始化方法,main栈帧被压倒下面
//main回到栈顶,的在main的栈帧中,会有一个Test类的引用,指向一个堆的地址
Test.staticHello();//会跳转去执行staticHello()方法,main栈被压到下面
//staticHello()返回,main成为栈顶
test.hello();//会跳转去执行hello()方法,main栈帧压入栈
//hello()返回,main成为栈顶
//main也完成,弹出
}
}
package com.test;
/**
* Created by RuiDu on 2016/11/22.
*/
//基类,会被优先初始化
public class BaseTest {
private int base;//虽然是private,子类不可达,但是还是被初始化了,是实例test的堆的内存的一部分
}
package com.test;
/**
* Created by RuiDu on 2016/11/22.
*/
public class Test extends BaseTest {
//下面这三个变量才是堆中存的值,此外还有父类的 private int base
private String testString = "haha"; //会被初始化为"haha"
private String testNotInit; //会被初始化为空,但是还是占用了堆的空间
private ObjectTest objectTest; //会被初始化为空,但是还是占用了堆的空间
//进入了staticHello()的栈帧
public static void staticHello(){
int i;//没有被使用,
int b = 1;//会被放在栈帧中
String c = "c";//会被放在栈帧中
ObjectTest objectTest = new ObjectTest();//开始ObjectTest()的初始化方法,staticHello()不再是栈顶
//返回,staticHello()变回栈顶
objectTest.hello();//开始objectTest.hello()的方法,staticHello()不再是栈顶
}
//非静态方法多了一个this传入,其余的差不多
public void hello(){
int i;
int b = 1;
ObjectTest objectTest = new ObjectTest();
objectTest.hello();
}
}
相关文章
- 暂无相关文章
用户点评