欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

Java的内存管理(一),Java内存管理(

来源: javaer 分享于  点击 20914 次 点评:15

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();
    }
}

相关文章

    暂无相关文章
相关栏目:

用户点评