Java阵列使用多少内存?
我试图找出一个数组在JVM中使用了多少内存。我为此制定了一个计划,这给了我奇怪的结果Java阵列使用多少内存?,java,arrays,profiling,Java,Arrays,Profiling,我试图找出一个数组在JVM中使用了多少内存。我为此制定了一个计划,这给了我奇怪的结果 protected static long openMem(){ System.gc(); System.runFinalization(); return Runtime.getRuntime().freeMemory(); } public static double listSize(int limit){ long start= openMem(); Objec
protected static long openMem(){
System.gc();
System.runFinalization();
return Runtime.getRuntime().freeMemory();
}
public static double listSize(int limit){
long start= openMem();
Object[] o= new Object[limit];
for(int i= 0; i<limit; i++ ){
o[i]= null;
}
long end= openMem();
o= null;
return (start-end);
}
public static void list(int i){
for(int y= 0; y<50; y++ ){
double d= Quantify.listSize(i);
System.out.println(i+" = "+d+" bytes");
}
}
public static void main(String ... args){
list(1);
list(2);
list(3);
list(100);
}
当我运行此命令时,对于每个大小的数组,我会得到两个不同的字节大小,如:
1=24.0字节
1=208.0字节
1=24.0字节
1=208.0字节
1=208.0字节
1=208.0字节
1=208.0字节
1=24.0字节
因此,1个元素的数组只返回24个字节或208个字节,其他所有元素的模式相同:
1=24.0字节
1=208.0字节
2=24.0字节
2=208.0字节
3=32.0字节
3=216.0字节
100=416.0字节
100=600.0字节
我在想为什么会这样。我想知道的是,这里的其他人是a已经知道答案,还是b知道如何找到答案。在JVM上测量堆占用率甚至比测量性能更棘手。首先,有线程本地分配缓冲区TLAB,它们是一次分配的堆块,与分配的对象大小无关。您应该禁用它们用于测量:-XX:-UseTLAB。此外,您的代码在某些方面是正确的,但在其他方面几乎是正确的。例如,我建议运行两个GC;无需运行终结;并在分配前运行GC,然后在解除分配后运行GC。仅在每次测量之前运行它。您还需要使用totalMemory freeMemory,否则易受堆大小调整的影响 总之,试着用这个代码测量,它会给我可靠的结果
class Quantify {
static final Object[][] arrays = new Object[20][];
static long takenMem(){
final Runtime rt = Runtime.getRuntime();
return rt.totalMemory() - rt.freeMemory();
}
static long arraySize(int size){
System.gc(); System.gc();
long start = takenMem();
for (int i = 0; i < arrays.length; i++) arrays[i] = new Object[size];
final long end = takenMem();
for (int i = 0; i < arrays.length; i++) arrays[i] = null;
System.gc(); System.gc();
return (end - start) / arrays.length;
}
public static void main(String... args) {
for (int i = 1; i <= 20; i++) System.out.println(i+": "+arraySize(i));
}
}
这与实际情况一致:由于报头的开销,最小分配为24字节;由于内存对齐问题,大小会更改8这是64位JVM的典型情况。我从不建议通过freemem增量测量任何内容。创建一些类型化数组,例如MyClass[],然后运行jmap-histo:live。内存无论如何都不是确定性的,Java运行时可能有自己的缓存和对象、线程,因此您可以通过freemem的增量来量化任何内容,您可能会得到当前已使用或可用内存的大致数字,但尝试测量像数组这样的单个对象是徒劳的。数组使用对象标头+4bytes length+sun.misc.Unsafe.ADDRESS_SIZE*array.length更简单的度量方法是jmap-Histori使用VisualVM的这些度量值时,它可能在内部依赖于jmap或类似的度量:它没有考虑压缩的OOP。这向我表明,它的报告并非来自直接测量,而是来自一些硬编码的大小数据,这些数据可能与实际情况不同步。jmap和jstack使用调试接口连接到jvm,visualvm可能使用相同的接口。visualvm不太喜欢visualvm,因为它不能真正用于生产。但是,所有快照都是在主机进程中执行的,因此如果不考虑压缩,则可能是某个错误。总的来说,我强烈建议您掌握jmap,因为它是分析生产服务器内存分配/使用和泄漏的不可或缺的工具。顺便说一句,有两种不同的模式-一种是直方图模式,另一种是内存转储模式。直方图是在主机进程中执行的,内存转储正确地使用任何估计大小。@bestsss是的,我们也使用jmap进行生产分析。我们也设法使用了VisualVM,但是为所有端口设置SSH隧道是一件非常痛苦的事情,这些端口没有确切的文档记录,有些甚至在运行之间进行了更改。但从VisualGC的角度来看生产服务器是一种全新的体验:
1: 24
2: 24
3: 32
4: 32
5: 40
6: 40
7: 48
8: 48
9: 56
10: 56
11: 64
12: 64
13: 72
14: 72
15: 80
16: 80
17: 88
18: 88
19: 96
20: 96