JVM的相关知识还是挺多的~~~读书后特地都记下来~~~要不很容易就忘掉额。。。

一个JVM实例包括一个方法区,栈内存,堆内存,本地方法区,PC寄存器。

JVM内存管理_jvm

执行引擎(作用:解析jvm字节码指令),每一个执行引擎实例是一个java线程。一个jvm实例中会同时有多个执行引擎在工作,这些引擎有的在执行用户程序,有的在执行jvm内部的程序(如gc)

JVM内存管理_堆_02

每创建一个执行引擎实例会给这个实例创建一个java栈和pc寄存器;如果当前正在创建一个java方法,那么当前的java栈中会保存:该线程中方法调用的状态(方法的参数,方法的局部变量,方法的返回值和运算中的中间结果)

如下面的这个方法:

public int hello(int a,int b){
    int x = a;
    int y = b;
    int sum = x+y;
    return sum;
}

那么我们的栈中就会保存:方法的参数a和b,局部变量x和y,中间结果sum=x+y,和返回的sum值。

如果我们是在main函数中调用的话,假设逻辑如下:

public static void main(String []args){
    int a = 4,int b = 8;
    int sum = hello(a,b);
    System.out.println(sum);
}

PC寄存器执行到hello(a,b)这个函数后,出栈以后指向即将执行的下一条指令println();

注意:如果是在本地方法调用则存储在本地方法调用栈中或者特定实现中的某个内存区域中。


在再往下面举例之前先好好地总结一下一些简单的知识点:

1.方法区(用于存储类的结构信息):

常量池、域、方法数据、方法体、构造函数、包括类中的专用方法、实例初始化、接口初始化都存储在这个区。

这个就是我们常说的java堆中的永久区。这个区域可以被所有线程共享,并且它的大小可以通过参数设置。

会占用java堆的内存,受gc控制。

存放装载类的数据信息包括:

(1)基本信息:

1.每个类的全限定名

2.每个类的直接超类的全限定名

3.该类是类还是接口

4.该类的访问修饰符

5.直接超接口的全限定名的有序列表

(2)每个已装载类的详细信息

1.运行时常量池:

存放该类型所用的一切常量(直接常量和对其他类型、字段、方法的符号引用),它们以数组形式通过索引被访问。

2.字段信息

类中声明的每一个字段的信息(名,类型,修饰符)

3.方法信息

类中声明的每一个方法的信息(名,返回类型,参数类型,修饰符,方法的字节码和异常表)

4.静态变量

5.到类classloader(类装载器)的引用

6.到类class的引用(jvm为每个被装载的类型创建一个class实例,用来代表这个被装载的类)

2.栈内存(栈和线程是对应起来的,不是线程共享)

java栈内存以帧的形式存放本地方法的调用状态(包括方法调用的参数,局部变量,中间结果等)。每调用一个方法就将对应该方法的方法帧压入java栈,成为当前方法帧。当调用结束(返回)时,就弹出该帧。

编译器将源代码编译成字节码(.class)时就已经将各种类型的方法的局部变量,操作数和栈大小确定并放在字节码中,随着类一并装载如方法区。当调用方法时,通过访问该方法区中的类的信息,得到局部变量以及操作数栈的大小。

在方法中定义的一些基本类型变量和对象的引用变量都在方法的栈内存中分配。当超过变量的作用域后,java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

栈内存构成:局部变量区,操作数栈,帧数据区组成。

帧数据区处理常量池解析、异常处理等


3.堆内存(堆会被所有线程共享,需要注意同步问题)

堆内存存放由new创建的对象和数组。在堆中分配的内存,有java虚拟机的自动垃圾回收器来管理。在堆中产生一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。引用变量就相当于为数组或对象起的一个名称,以后就可以在程序中使用栈的引用变量来访问堆中的数组或对象。

其中java的引用数据类型包括:类,接口,数组

关于引用类型的内存分配,觉得这篇blog写得挺简明易懂的~~可以参考一下

http://liu1227787871.blog.163.com/blog/static/205363197201263103320466/


4.栈内存和堆内存的比较

与c++不同,java自动管理栈和堆,程序员不能直接地设置栈和堆。java堆是动态分配内存大小的,生存期也不用事先告诉编译器,因为它是在运行时动态分配内存的。所以堆的存取速度较慢。

栈的优势是存取速度很快,仅次于寄存器。但数据的大小和生存期是确定的。

栈中的数据可以共享的,具体表现为:

int a = 3;
int b = 3;

首先在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没有,把3存进来,然后a指向3,接着处理int b = 3;创建完b的引用变量后,如果在栈中已经有3这个值。

如果再加上一句b =4;(如右图,b的值不会影响a的值)

JVM内存管理_方法区_03JVM内存管理_内存管理_04

注意:这种数据的共享和两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改不会影响到b,它是由编译器完成的。


5.本地方法栈内存

和调用的语言相关,如果调用的是一个c语言的方法则为一个c栈,java通过java本地接口JNI(java native interface)来调用其他语言编写的程序,在java里面用native修饰符莱描述一个方法是本地方法。