Java虚拟机的内存分配

Java虚拟机里的内存分配了许多区域用于存储不同的内容,这里就作者所学及其理解发表看法,如有错误欢迎指出。

在《深入理解Java虚拟机》这本书中对Java虚拟机内存在运行时数据区域有下面的图示。其中绿色区域是线程共享的,黄色区域是线程私有的。

但是为什么要使用运行时数据区域呢?

因为在这个时候Java虚拟机的内存才能完全被使用,这时的内存分配才完整。

一、程序计数器

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

简单的来说就是一个记号位,它存储了一个程序当前执行区域的位置信息。因为在程序执行的过程中可能会发生异常中断或者是线程调度,这个时候程序在恢复执行的时候就得知道自己到底运行到了哪里,而这个信息就是程序计数器提供的。这也解释了为什么程序计数器是线程私有的。

 

二、虚拟机栈和本地方法栈

刚开始接触Java的时候,我们都应该对Java的内存分配有过这样的印象。那就是栈中存储变量和引用堆中存储对象信息,这里说的栈就是虚拟机栈,但是这样的描述并不精确。

虚拟机栈的生命周期跟线程相同,因为它是描述Java方法执行的内存模型,而Java线程是通过Runnale接口的run()方法创建的。

每个方法在执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,因此它是线程私有的也很好理解。

局部变量表存储了编译器可知的各种基本数据类型和对象引用。

操作数栈是后入先出,它可以接收数据和操作指令完成运算。

如图,B+A语句中,通过字节码操作指令,会让A和B先后出栈运算,然后将相加的结果入栈,等待稍后的操作(这里的栈时操作数栈,操作数栈是虚拟机栈的一部分)。

本地方法栈和虚拟机栈发挥的作用差不多,其区别就是虚拟机栈执行字节码服务,本地方法栈执行Native方法服务.

 

三、堆和方法区

堆里面存储的全部都是对象,每个对象都包含一个与之对应的class的信息(class的目的是得到操作指令)。

方法区又叫静态区,方法区包含所有的class和static变量。它所保存的都是整个程序中永远唯一的元素。

为什么这样说呢?

因为每个对象都应该能够调用自身的方法和变量(包括静态与非静态),知道反射机制的小伙伴应该都知道class信息是属于类的信息即类的类型信息,而这部分信息都保存在方法区中。在堆中我们只存储对象实例信息(非静态的变量和方法)和指向方法区中class信息的引用

访问一个对象内存图示:

当类被加载进内存并且未实例化对象之前,我们可以通过类名访问方法区中类的静态域和class信息。当类已经实例化了一个对象时,我们通过指向对象的reference访问对象的实例数据和访问类型数据。

堆和方法区都是线程共享的:

这一点如果知道线程锁机制的小伙伴应该不难理解。

public class TestOne {
    private static int a=0;
    /*
    * 给静态方法上锁
    * */
        public static synchronized void fun1(){
            a++;
        }
        /*
        * 给成员方法上锁
        * */
        public synchronized void fun2(){
            a++;
        }
        public void fun3(){
            /*
            * 给代码块上锁
            * */
            synchronized (this){
                a++;
            }
        }
}

在多线程并发执行时,如果我们要同步某个方法或代码块来获得正确的结果时,我们就必须给这个方法或代码块上锁,而一般我们都是给它提供对象锁来解决同步问题。因为是线程共享的,所以对象的锁只有一把,所以在同一时刻只有获取到锁的方法或代码段在运行。

posted @ 2018-12-05 18:48  问月晚安  阅读(119)  评论(0编辑  收藏  举报