我需要存储大量 RGB 颜色对象。对于一些常见用途,它们占用了我的应用程序总内存的 8% 到 12%。我目前将其定义如下:
class MyColor {
byte red;
byte green;
byte blue;
}
我假设(大多数)JVM 实际上对每个条目都使用 int。最简单的替代方案是:
class MyColor {
byte [] color = new byte[3];
private static final int red = 0;
private static final int green = 1;
private static final int blue = 2;
}
这会将整个数组放入一个 int 中吗?或者它是一个 int[3] ?如果是第一个,那就太好了。如果是第二种,那么最好的是:
class MyColor {
int color;
private static final int red_shift = 0;
private static final int green_shift = 8;
private static final int blue_shift = 16;
}
或者有更好的方法吗?
Update:我还将有一个 getRed()、setRed(int)...作为访问器。我只是列出了该类的数据组件以使其更小。和尺寸是这里的关键问题。该代码不会花费大量时间访问这些值,因此性能不是一个大问题。
更新2:我去运行这个使用效用大小 https://code.google.com/p/core-java-performance-examples/source/browse/trunk/src/main/java/com/google/code/java/core/sizeof/SizeofUtil.java(下面引用 - 谢谢)。我使用如下代码完成了此操作:
protected int create() {
MyColor[] aa = new MyColor[100000];
for (int ind=0; ind<100000; ind++)
aa[ind] = new MyColor2();
return 2;
}
}.averageBytes());
这就是事情变得奇怪的地方。首先,如果我不执行 for 循环,因此它仅创建数组(所有值均为空),那么它会报告 400016 字节或 4 字节/数组元素。我使用的是 64 位系统,所以我很惊讶这不是 800000(Java 在 64 位操作系统上有 32 位地址空间吗?)。
但接下来奇怪的部分来了。 for 循环的总数为:
- 2800016.0
- 2600008.0
- 2800016.0
第一个惊喜是,使用 byte[3] 的第二种方法使用更少的内存! JVM 在看到声明中的 byte[3] 后是否有可能只是内联分配它?
其次,每个对象的内存为 (2,800,000 - 400,000) / 100,000 = 24。我将购买第一种方法,其中每个字节都是本机 64 位 int。 3 * 8 字节 = 24 字节。但对于第三种情况,它是一个单一的整数?这是没有意义的。
代码在这里以防万一我错过了一些东西:
package net.windward;
import java.util.Arrays;
public class TestSize {
public static void main(String[] args) {
new TestSize().runIt();
}
public void runIt() {
System.out.println("The average memory used by MyColor1 is " + new SizeofUtil() {
protected int create() {
MyColor1[] aa = new MyColor1[100000];
for (int ind = 0; ind < 100000; ind++)
aa[ind] = new MyColor1();
return 1;
}
}.averageBytes());
System.out.println("The average memory used by MyColor2 is " + new SizeofUtil() {
protected int create() {
MyColor2[] aa = new MyColor2[100000];
for (int ind = 0; ind < 100000; ind++)
aa[ind] = new MyColor2();
return 2;
}
}.averageBytes());
System.out.println("The average memory used by MyColor3 is " + new SizeofUtil() {
protected int create() {
MyColor3[] aa = new MyColor3[100000];
for (int ind = 0; ind < 100000; ind++)
aa[ind] = new MyColor3();
return 1;
}
}.averageBytes());
System.out.println("The average memory used by Integer[] is " + new SizeofUtil() {
protected int create() {
Integer[] aa = new Integer [100000];
for (int ind = 0; ind < 100000; ind++)
aa[ind] = new Integer(ind);
return 1;
}
}.averageBytes());
}
public abstract class SizeofUtil {
public double averageBytes() {
int runs = runs();
double[] sizes = new double[runs];
int retries = runs / 2;
final Runtime runtime = Runtime.getRuntime();
for (int i = 0; i < runs; i++) {
Thread.yield();
long used1 = memoryUsed(runtime);
int number = create();
long used2 = memoryUsed(runtime);
double avgSize = (double) (used2 - used1) / number;
// System.out.println(avgSize);
if (avgSize < 0) {
// GC was performed.
i--;
if (retries-- < 0)
throw new RuntimeException("The eden space is not large enough to hold all the objects.");
} else if (avgSize == 0) {
throw new RuntimeException("Object is not large enough to register, try turning off the TLAB with -XX:-UseTLAB");
} else {
sizes[i] = avgSize;
}
}
Arrays.sort(sizes);
return sizes[runs / 2];
}
protected long memoryUsed(Runtime runtime) {
return runtime.totalMemory() - runtime.freeMemory();
}
protected int runs() {
return 11;
}
protected abstract int create();
}
class MyColor1 {
byte red;
byte green;
byte blue;
MyColor1() {
red = green = blue = (byte) 255;
}
}
class MyColor2 {
byte[] color = new byte[3];
private static final int red = 0;
private static final int green = 1;
private static final int blue = 2;
MyColor2() {
color[0] = color[1] = color[2] = (byte) 255;
}
}
class MyColor3 {
int color;
private static final int red_shift = 0;
private static final int green_shift = 8;
private static final int blue_shift = 16;
MyColor3() {
color = 0xffffff;
}
}
}