Tag: jvm

Understanding Java Virtual Machine(JVM) memory usage

January 4th, 2010

Concept
~~~~~~~~~~~
最近写程序需要了解一些JVM内存使用的知识,网上搜了一大圈,真是说什么的都有,还是老老实实在JVM规范SUN的论坛找答案吧。JVM使用的内存分为:

  • Stack Memory (栈内存) : 虚拟机的每一个线程都有一个私有的栈,当一个方法被调用时,下面内容被作为一个Frame (帧)被创建并且被压入栈中:
    + 局部变量:包括基本数据类型,对象的引用和返回值地址。
    + 一个自己的操作栈:帧内局部变量进行运算时使用,也用于传递方法的参数和接受方法的返回值。
    + 一个当前方法所在类的Runtime constant pool (常量池)的引用。
    方法调用完成时,帧出栈,并销毁,无论方法是正常结束还是有未捕获的异常。
  • Heap Memory(堆内存) : 虚拟机的堆内存保存的是对象,类变量以及实例变量,它被所有线程共享,常说的垃圾回收就是对堆内存的回收。
+-----------------------+
|    Stack Memory       | ----------> 线程私有
+-----------------------+
|         ^             | -------+
+---------|-------------+        |
|         |             |        |
+----Heap Memory--------+        |
|         |             |        |--> 线程共享
+---------|-------------+        |
|         v             |        |
+-----------------------+        |
|    Method Area        | -------+
+-----------------------+

当JVM加载一个class时 ,将该类的一些信息保存到Method Area,包括Runtime constant pool ,方法数据,方法和构造器代码,域等。Runtime constant pool 则包括类名,父类名,静态变量等。
Method Area在逻辑上属于Heap。不过它垃圾回收与Heap可能不同,取决于JVM的实现。
当通过new Class()方式创建一个实例时,JVM在Method Area寻址到该类的基本信息, 同时进行相关实例的初始化(包括实例变量),存贮在Heap中。

Example
~~~~~~~~~~~
栈内存比堆内存小,但是效率高,所以更珍贵。下面的例子可以证明这一点,如何合理的配置和使用这两个内存,让应用的效率更高,是一个大命题,只能在实践中慢慢摸索。

private int x=0;
 
public void run() {
    // 取stack运算的时间差(纳秒)
    long start = System.nanoTime();
    stackAccess();
    long end = System.nanoTime() - start;
    System.out.println("stackAccess:" + end);
    // 取heap运算的时间差(纳秒)
    start = System.nanoTime();
    heapAccess();
    end = System.nanoTime() - start;
    System.out.println("heapAccess:" + end);
}
 
public void stackAccess() {
    // 减少堆寻址的次数,提高效率
    x = 0; // x 是一个实例变量,存贮在堆中
    int j = x; // j 是一个局部变量,存贮在栈中
    for (int i = 0; i < 100; i++) {
        j += 1;
    }
    x = j;
}
 
public void heapAccess() {
    // 每次循环x都去堆中寻址,降低效率
    x =0;
    for (int i = 0; i < 100; i++) {
        x += 1;
    }
}

静态变量是否会被垃圾回收?
这是一个网上讨论异常激烈的问题,我是这样理解的:
类的静态变量(类变量)存贮在Method Area(具体点说在Runtime Constant Pool)中,如果把它置为null,那么它所引用的对象会被GC。类变量自身不会被回收,除非类被卸载,也就是说:即使这个类的所有的实例都被回收,该类变量仍然存在,即使它引用一个空的对象。

什么时候使用静态变量?
不用我多说(各种教程都有介绍),如果不想用静态变量,也可以尝试单态或者线程同步来实现对象共享,例如:

Logger logger = LoggerFactory.getLogger(ClassA.class);

在这种情况下,可以不用将logger置为static类型了。

由于水平有限,感觉JVM规范太抽象,对于具体实现介绍的不够详细,希望理解的没大问题。

Reference
~~~~~~~~~~~
[1] The Structure of the Java Virtual Machine
[2] Sun Forums > Desktop > Runtime Environment
[3] Picture-Mode

Tags:
Posted in Technology | No Comments »