JVM 学习-内存管理

 

2 JVM的内存管理 

所有的数据和程序都是在运行数据区存放,它包括以下几部分: 

 

  • Stack 栈 
栈也叫栈内存,是 Java程序的运行区,是在线程创建时创建,它的生命期是跟随线程的生命 期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束,该栈就 Over。 问题出来了:栈中存的是那些数据呢?又什么是格式呢?

 

栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是 一个有关方法(Method)和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧 F1,并 被压入到栈中,A方法又调用了 B方法,于是产生栈帧 F2 也被压入栈,执行完毕后,先弹出 F2 栈帧,再弹出 F1 栈帧,遵循“先进后出”原则。

那栈帧中到底存在着什么数据呢?栈帧中主要保存3 类数据:本地变量(Local Variables), 包括输入参数和输出参数以及方法内的变量;栈操作(Operand Stack),记录出栈、入栈的操作; 栈帧数据(Frame Data),包括类文件、方法等等。光说比较枯燥,我们画个图来理解一下 Java 栈,如下图所示:

 

图示在一个栈中有两个栈帧,栈帧 2是最先被调用的方法,先入栈,然后方法 2 又调用了方法1,栈帧 1处于栈顶的位置,栈帧 2 处于栈底,执行完毕后,依次弹出栈帧 1和栈帧 2,线程结束,栈释放。 

 

  • Heap 堆内存 

 

一个 JVM 实例只存在一个堆类存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行,堆内存分为三部分:
Permanent Space  永久存储区

永久存储区是一个常驻内存区域,用于存放 JDK 自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。 

Young Generation Space  新生区 

新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。新生区又分为两部分: 伊甸区(Eden space)和幸存者区(Survivor pace) ,所有的类都是在伊甸区被new出来的。幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存 0区。若幸存 0区也满了,再对该区进行垃圾回收,然后移动到 1 区。那如果1 区也满了呢?再移动到养老区。 

Tenure generation space 养老区

养老区用于保存从新生区筛选出来的 JAVA 对象,一般池对象都在这个区域活跃。 三个区的示意图如下:

 

  • Method Area 方法区 
 方法区是被所有线程共享,该区域保存所有字段和方法字节码,以及一些特殊方法如构造函数, 接口代码也在此定义。 
  • PC Register 程序计数器
 每个线程都有一个程序计数器,就是一个指针,指向方法区中的方法字节码,由执行引擎读取 下一条指令。  

 

  • Native Method Stack 本地方法栈
 以上都是纯理论,我们举个例子来说明JVM的运行原理,我们来写一个简单的类,代码如下:

 

 1 public class JVMShowcase { 
 2  //静态类常量, 
 3  public final static String ClASS_CONST = "I'm a Const"; 
 4  //私有实例变量 
 5  private int instanceVar=15; 
 6  public static void main(String[] args) { 
 7    //调用静态方法 
 8    runStaticMethod(); 
 9    //调用非静态方法 
10   JVMShowcase showcase=new JVMShowcase(); 
11   showcase.runNonStaticMethod(100); 
12  }  
13  //常规静态方法 
14  public static String runStaticMethod(){ 
15    return ClASS_CONST; 
16  } 
17  //非静态方法 
18  public int runNonStaticMethod(int parameter){ 
19    int methodVar=this.instanceVar * parameter; 
20    return methodVar; 
21  } 
22 } 
这个类没有任何意义,不用猜测这个类是做什么用,只是写一个比较典型的类,然后我们来看看JVM是如何运行的,也就是输入 java JVMShow后,我们来看JVM是如何处理的: 
第 1 步,JVM 向操作系统申请空闲内存 。JVM对操作系统说“给我 64M空闲内存”,于是操作系统就查找自己的内存分配表,找了段 64M的内存写上“Java占用”标签,然后把内存段的起始地址和终止地址给JVM,JVM准备加载类文件。 
第 2 步,JVM 内存。JVM获得到 64M内存,就开始得瑟了,首先给 heap分个内存,并且是按照 heap的三种不同类型分好的,然后给栈内存也分配好。 
第 3 步,检查和分析class 文件。若发现有错误即返回错误。 
第 4 步,加载类。由于没有指定加载器,JVM默认使用 bootstrap 加载器,就把 rt.jar下的所有类都加载到了堆类存的永久存储区,JVMShow也被加载到内存中。我们来看看栈内存,如下图:

Heap是空,Stack是空,因为还没有线程被执行。Class Loader通知Execution Enginer已经加 载完毕。

第 5 步,执行引擎执行 main 方法。执行引擎启动一个线程,开始执行 main 方法,在 main 执 行完毕前,方法区如下图所示:

 

在 Method Area加入了CLASS_CONST 常量,它是在第一次被访问时产生的。堆内存中有两个对象 object和 showcase对象,如下图所示:

为什么会有 Object对象呢?是因为它是JVMShowcase的父类,JVM是先初始化父类,然后再 初始化子类,甭管有多少个父类都初始化。在栈内存中有三个栈帧,如下图所示: 

 

于此同时,还创建了一个程序计数器指向下一条要执行的语句。 

第 6 步,释放内存。运行结束,JVM向操作系统发送消息,说“内存用完了,我还给你”,运行结束。

posted @ 2012-02-21 23:36  勤劳的双手  阅读(382)  评论(0编辑  收藏  举报