代码改变世界

JAVA静态域及容器的内存占用探究

2014-03-16 12:23  风流小探花  阅读(1195)  评论(0编辑  收藏  举报

1. 第一个问题:

我们知道JVM中对于静态变量的存储是在方法区中(permGen),那么当我们声明一个类的静态变量并且立即初始化为该变量为一个对象时,该对象是存在哪里的呢?

PermGen中是只存了该对象的引用还是将该对象的实际占用空间也存储了?如果对象实际内存在permGen,对于该区往往分配内存较少,岂不是很容易就抛出异常 out of Memery error PermGen space,我想答案应该是否定的,但是这需要我们试验验证一下!

2. 另外一个问题:

对于JAVA中的数据结构各种容器类(List Set Map)的内存分配问题,我们知道 当我们采用无参构造 list = new ArrayList();会默认分配一个默认容量16的list,当元素超过该值,再以0.75的比例扩容;既然有容量设置,那么就肯定会在内存中预先申请一块内存空间!

那么这16大小的list每个位置申请的空间是存储对象的实际大小的空间,还是只是一个 对象引用的空间呢!如果是前者,那么我们以后不得不小心容器类带来的内存浪费问题,采用指定容器大小的构造list = new ArrayList(16); 当然我想前者概率应该很低,因为对象大小在添加前,list是不可知该对象大小的,当然这些都是我们的推论,那么让我们做个试验!

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class StaticFieldTest {
//    public static byte[] bytes = new byte[100000000];//静态变量对应的对象也是占用堆空间(100M内存),而非方法区(permGen)
    
    public static void main(String[] args) throws Exception {
//        testList(); //测试list内存占用
//        testMap(); //测试map内存占用
        testListReferenceAndStorage();//测试list中存储的引用还是对象
        Thread.sleep(100*1000); //保持100s好打开jconsole看
        
    }
    
    public static void testList(){
        ArrayList list = new ArrayList(100000000); //100M list,占用400M 堆内存
    }
    
    public static void testMap(){
        Map map = new HashMap(100000000);//100m map.占用533m堆内存
    }
    
    public static void testListReferenceAndStorage(){
        ArrayList list = new ArrayList(10000000); //10M list,应该占用40M 堆内存
        for(int i=0;i<10000000;i++){   //add对象后,共占用对内存369M,很遗憾产生了GC,但是结果已经显而易见
            list.add(new byte[10]); 
        }
    }
}

结论:

1. 对象实际占用内存空间总是在堆中,即使他作为其他类的静态变量!

2. 容器类(list map等)声明时,总是预先占用指定大小的空间,该空间只是引用空间,不是其元素对象的实际占用对空间和