这个程序中size因为用final修饰,不能再修改它的值,所以成为常量,而这常量将会存放在常量区,这些常量在编译时知道占用空间的大小,但并不是说明该区域编译固定了,运行期也可以修改常量池的大小,典型的场景是在使用String时,你可以调用String的 intern(),JVM会判断当前所创建的String对象是否在常量池中,若有,则从常量区取,否则把该字符放入常量池并返回,这时会修改常量池的大小,比如JDK中java.io.ObjectStreamField的一段代码:


....
ObjectStreamField(Field field, boolean unshared, boolean showType) {
this.field = field;
this.unshared = unshared;
name = field.getName();
Class ftype = field.getType();
type = (showType || ftype.isPrimitive()) ? ftype : Object.class;
signature = ObjectStreamClass.getClassSignature(ftype).intern();
}


  这段代码将获取的类的签名放入常量池。HotSpot中并没有单独为该区域分配,而是合并到方法区中。

  3、直接内存区。直接内存区并不是JVM可管理的内存区。在JDK1.4中提供的NIO中,实现了高效的R/W操作,这种高效的R/W操作是通过管道机制实现的,而管道机制实际上使用了本地内存,这样避免了从本地源文件复制JVM内存,再从JVM复制到目标文件的过程,直接从源文件复制到目标文件,JVM通过DirectByteBuffer操作直接内存。

  4、堆。主角总是后出场,堆是JVM中的一等公民,的主角,我们通常所说的GC主要是在这块区域中进行的,所有的java对象都在这里分配,这也是JVM中大的内存区域,被所有线程共享,成千上万的对象在这里创建,也在这里被销毁。

  java内存分配到这算是一个完结了,接下来我们将讨论java内存的回收机制,

  内存回收主要包含以下几个方面理解:

  第一,局部变量占用内存的回收,所谓局部变量,是指在方法内创建的变量,其中变量又分为基本类型和引用类型。如下代码:


...
  public void test()
{
int x=1;
char y='a';
long z=10L;
}


  变量x y z即为局部变量,占用的空间将在test()所在的线程栈中分配,test()执行完了后会自动从栈中弹出,释放其占用的内存,再来看一段代码:


....
public void test2()
{
Date d = new Date();
System.out.println("Now is "+d);
}


  我们都知道上述代码会创建两个对象,一个是Date d另一个是new Date。Date d叫做声明了一个date类型的引用,引用是一种类型,和int x一样,它表明了这种类型要占用多少空间,在java中引用类型和int类型一样占用4字节的空间,如果只声明引用而不赋值,这4个字节将指向JVM中地址为0的空间,表示未初始化,对它的任何操作都会引发空指针异常。

  如果进行赋值如d = new Date()那么这个d保存了new Date()这个对象的地址,通过之前的内存分配策略,我知道new Date()是在jvm的heap中分配的,其占用的空间的回收我们将在后面着重分析,这里我们要知道的是这个Date d所占用的空间是在test2()所在的线程栈分配的,方法执行完后同样会被弹出栈,释放其占用的空间。

  第二,非局部变量的内存回收,在上面的代码中new Date()和C++里的new创建的对象一样,是在heap中分配,其占用的空间不会随着方法的结束而自动释放需要一定的机制去删除,在C++中必须由程序员在适当时候delete掉,在java中这部分内存是由GC自动回收的,但是要进行内存回收必须解决两问题:那些对象需要回收、怎么回收。判定那些对象需要回收,我们熟知的有以下方法:

  一,引用计数法,这应是绝大数的的java 程序员听说的方法了,也是很多书上甚至很多老师讲的方法,该方法是这样描述的,为每个对象维护一个引用计数器,当有引用时加1,引用解除时减1,那些长时间引用为0的对象判定为回收对象,理论上这样的判定是准确的,判定的效率也高,但是却有一个致命的缺陷,请看以下代码:


package com.mail.czp;

import java.util.ArrayList;
import java.util.List;

public class Test {

private byte[] buffer;
private List ls;

public Test() {
this.buffer = new byte[4*1024*1024];
this.ls = new ArrayList();
}
private List getList() {
return ls;
}

public static void main(String[] args) {
Test t1 = new Test();
Test t2 = new Test();
t1.getList().add(t2);
t2.getList().add(t1);
t1 = t2 = null;
Test t3 = new Test();
System.out.println(t3);
}
}