背景说明:
最近工作中有一个业务,暂时考虑将每条记录对象放入内存,目标峰值定为50W条数据(50W个对象)。
在使用jvisualvm监控中发现,在内存中Bean实例数为50W,大小为4000W字节整(不是大约,是详细数据),但是在堆中显示的是实例数50W,大小为6000W字节(同上)。
在查阅《深入理解java虚拟机》中发现下图,我怀疑是否是因为jvisualvm监控中显示的大小为实例对象的大小,不包含对象中包含的属性和对象。但为何无论空对象,还是有赋值的对象实例堆大小和内存大小不一致。我想问下各位,内存和堆中大小为何不一致。

解决方案 »

  1.   

    你看这个吧,不知道是不是这个的原因。。
    这里说JVM 本身也要占用内存,就像 OS 内核和 C 运行时一样,而 JVM 占用的内存是本机堆的子集。 
    这个也挺好验证的,楼主内存中Bean实例数为10W的时候看堆大小就可以了
    http://www.ibm.com/developerworks/cn/java/j-codetoheap/
      

  2.   

    你这个问题比较复杂,你给的信息太少,50w个对象放在内存中?以何种方式放在内存?单纯的new还是static静态list然后放置在list中?进程状态是什么样子的?对象随着进程的变化是否会产生变化?你最好把jvisualvm的截图和heapdump发出来,根据的你内存具体变化才能看出具体情况
    还有用jvisualvm的visual GC插件看过具体的新生代老年代么
    你给的信息太少,猜都不好猜
      

  3.   

    昨天又测试了一遍                HashSet<AbnorAccountBean> hm = new HashSet<>();
    int forNumber = 500000;
    for(int i = 0; i < forNumber ; i++){
    AbnorAccountBean bean = new AbnorAccountBean();
    hm.add(bean);
    }Bean对象的属性数量增加,减少都会影响大小。无论有没有值。
    我差不多理解的就是显示的对象大小只记录的是引用大小
    比如一个int = 4B
    如果Bean里面有10个int 对象的大小就是40B + 他自身的引用大小 
    如果Bean里面还有别的引用,也只计算引用的大小
      

  4.   

    jvisualvm就是专门监控虚拟机中运行的应用的,和本地堆栈无关,监控的就是虚拟机中运行的应用的信息,比如你开的一个Eclipse,初始加载1W的类,50个线程等等信息都可以看到。
      

  5.   

    除了1楼说的以外,会不会是对象中还有一些别的引用也算上了,比如说字符常量的大小等等。
    字符串由专门的一个String和char[]统计,类里面加载的String只记录的是String引用的大小。
    所以最后只能大概估算了一下,50W全部属性赋值的对象,基本变量大小≈57M 字符常量≈6M time≈16 在加上其他一些对象 50W全部属性赋值的对象大概100M。
    简单点说,对象的大小就是对象内所以属性的引用大小的和。
    方法区,类加载区的大小没计算。
      

  6.   

    这个不好计算啊,你有个String是"12345",占用堆空间的字节绝不止5个字节。
      

  7.   

    用处就是对象内存占用是否过高,是否会影响系统运行。
    比如说我这个功能如果放入内存要占100M内存,如果放数据库,每天50W数据,保存30天就是1500W数据,
    你每次查数据你觉得是在内存中使用好,还是每次从数据库查好,哪种对系统的压力要小?
    各种优缺点都知道,才能正确的根据项目需求确定业务的解决方案啊。
    简单就是效率,速度,服务器硬件压力之间的平衡点。
    我居然为了你3个字回复这么多。是不是很荣幸
      

  8.   

    看了你后面的回复,我感觉50w个对象放入内存中这个解决方式比较蛋痛,你还是果断放到数据库吧,30天的话考虑下按星期分表每周一定期删除是比较合适的
    如果非要将50w的对象放入内存,还是推荐将这些数据放入方法区吧(设为静态static),这样不会影响minor GC也不会影响Full GC,也就是说对你的堆内存没什么影响了,缺点就是需要较大的方法区
    还是放到数据库吧,对mysql还有oracle这种数据库来说,加好索引200w的数据是毫无压力的
      

  9.   

    学艺不精啊,事实上只能将hashset设置为静态,其中存放的对象还是存在java堆中,由于我估计这些对象的生存周期都比较长,估计最终还是会晋升至老年代,所以你的老年代设置的要大一些(-Xms -Xmx -Xmn),而且Full GC时由于你的老年代至少存放了50w个对象,所以老年带最好换成CMS收集器(当然这个收集器很吃CPU),并且你的服务器最好在在4核以上(物理核+逻辑核),总之要注意的还是很多的
      

  10.   

    这个只是测试类,没有在意那些细节。现在32CPU,VM 4G放内存确实不是很合适,在试试数据库吧,测试下数据库的连接情况。
      

  11.   

    我就随便说说 你就随便听听:
    文件的实际大小和占用空间是两个不同概念 文件的大小是以字节byte计量的 而占用空间是以簇计量的
    为了更好地管理磁盘空间和更高效地从硬盘读取数据,操作系统规定一个簇中只能放置一个文件的内容,因此文件所占用的空间,只能是簇的整数倍;而如果文件实际大小小于一簇,它也要占一簇的空间。所以,一般情况下文件所占空间要略大于文件的实际大小,只有在少数情况下,即文件的实际大小恰好是簇的整数倍时,文件的实际大小才会与所占空间完全一致。 
      

  12.   

    对象存放在内存中不是什么太大的问题(只要内存够用),主要是对内存GC的影响,影响了GC就会影响整体性能,应该更多的关注存放50w数据前后对GC的影响
    加上这两个参数-XX:+PrintHeapAtGC -XX:+PrintGCDetails,然后看看GC的耗时,内存的拷贝情况吧