我需要存储大量的RGB颜色对象。这些占8%和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;
}
或者有更好的方法吗?
更新:我还有一个getRed(),setRed(int),...作为访问者。我刚刚列出了类的数据组件以使其更小。 大小是这里的关键问题。代码不会花费大量时间来访问这些值,因此性能不是一个大问题。
更新2:我使用SizeofUtil进行了操作(请参阅下文 - 谢谢)。我使用以下代码执行此操作:
protected int create() {
MyColor[] aa = new MyColor[100000];
for (int ind=0; ind<100000; ind++)
aa[ind] = new MyColor2();
return 2;
}
}.averageBytes());
这就是奇怪的地方。首先,如果我不执行for循环,那么它只创建数组(所有值为null),然后它报告400016字节或4字节/数组元素。我在64位系统上,所以我很惊讶这不是800000(Java在64位O / S上有32位地址空间吗?)。
然后是奇怪的部分。 for循环的总数是:
首先出乎意料的是,使用byte [3]的第二种方法使用更少的内存!是否有可能JVM看到声明中的字节[3],只是将其内联分配?
其次,每个对象的内存是(2,800,000 - 400,000)/ 100,000 = 24.我会为第一种方法购买它,其中每个字节都是一个原生的64位int。 3 * 8字节= 24字节。但对于第三种情况,它是一个单一的int?这毫无意义。
这里的代码,以防我错过了一些东西:
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;
}
}
}
答案 0 :(得分:4)
由于四个byte
适合int
,您可以使用一个int
作为您的颜色(如果您愿意,还可以为byte
留出额外空间添加,比方说,alpha,稍后)。示例一小组方法(未经测试,只是为了让您明白这一点):
public int toIntColor(byte r, byte g, byte b) {
int c = (int) r;
c = (c << 8) | g;
c = (c << 8) | b;
return c;
}
要获得字节:
public byte red(int c) {
return c >> 16 & 0xFF;
}
public byte green(int c) {
return c >> 8 & 0xFF;
}
public byte blue(int c) {
return c & 0xFF;
}
答案 1 :(得分:1)
你的第一种方法似乎比其他两种更好。它将在64位JVM上占用16个字节,在32位上占用12个字节。第二是最贵的,第三是16字节。
如果您要存储图像,还可以将颜色存储在byte[width][height]
的三个矩阵中,这样可以节省大量的字节。这个想法是放弃MyColor
类,每个实例需要额外的13个字节,从而节省大约80%的内存。
答案 2 :(得分:0)
将每种颜色存储为整数数组中的RGB int:
int[] colors;
它高效且非常方便。你也可以通过使用一个字节数组来保存每种颜色的另一个字节(25%),但这样不太方便,而且可能不值得。
如果使用任何类型的MyColor对象,则在对象标题上浪费至少8个字节,在对象引用上至少有4个字节,然后才能开始存储颜色数据本身。
我假设(大多数)JVM实际上为每个条目使用了一个int。
不,它们是真正的字节,虽然它会占用4个字节的空间,而不是3个字节,因此它需要与int字段相同的空间。
byte[] color = new byte[3];
效率最低。在计算实际的数组数据之前,该数组是一个单独的对象,数组对象头需要至少8个额外字节,length
字段需要4个字节,对它的引用需要4个字节。
答案 3 :(得分:0)
class MyColor {
byte red;
byte green;
byte blue;
}
为每种颜色创建一个新对象,同时具有作为对象[1]的内存开销。
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;
}
不太理想,因为你有两个对象,一个byte []和一个MyColor。这使开销增加了一倍。我所知道的没有优化可能会将byte []转换为int。
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;
}
它仍然具有与基于字节的MyColor相同的对象开销,并且还具有不断变换位的开销。
我会推荐类似的东西:
class MyColor{
byte getR(int col){...}
byte getG(int col){...}
byte getB(int col){...}
int getCol(byte r, byte g, byte b){...}
}
类型安全性差,但它具有最小的开销,可以像https://stackoverflow.com/a/20443523/2299084建议的那样存储在数组中。
单个字节不会占用int的空间,如一个简单的测试程序所示:
public class Test{
public static byte[] bigarr = new byte[100000];
public static void main(String[] args) {
try{Thread.sleep(100000);}catch(Exception e){}
}
}
和分析器:这是使用Java HotSpot(TM)64位服务器VM(24.45-b08,混合模式)。
答案 4 :(得分:0)
这完全取决于您想要存储的颜色深度。假设您有24位颜色深度,即8位红色8位绿色和8位蓝色,那么您只能将所有三个值存储在一个整数中。因为java整数是32位。
所以简单定义:
int colorValue = 0; //RGB Composite color value with 24 bit depth.
现在您要将所有颜色分量存储在一个整数中。这需要一些操作技巧。我们假设您以这种格式存储整数:
<00> 00000000BBBBBBBBGGGGGGGGRRRRRRRR(R,G和B各为8位)。然后,您需要以下功能:int getRed(int colorVal)
{
return colorVal & 127; //Gives the last 8 bits
}
int getGreen(int colorVal)
{
return (colorVal >> 8) & 127; //Gives the middle 8 bits
}
int getBlue(int colorVal)
{
return (colorVal >> 16) & 127; //Gives the first 8 bits
}
int getColorVal(int red, int green, int blue)
{
return (blue << 16) | (green << 8) | red;
}
现在存储你的大量颜色只需声明许多整数:
int width = <WIDTH>;
int height = <HEIGHT>;
int colorData[width * height];
希望你现在能理解它。
答案 5 :(得分:0)
其他答案都没有考虑的一个基本问题是,您是否希望 是RGB三元组或识别 > RGB三联体。考虑表单上两个文本对象的场景;表单的背景颜色指定为红色。其中一个文本对象的背景颜色被指定为与表单相同;其他的被指定为红色。尽管两个文本对象的背景都呈现相同的红色,但是一个对象将具有窗体的背景颜色,而另一个对象将具有其属性匹配的独立颜色。如果表单的背景颜色要更改为绿色,则其中一个文本对象将继续与表单颜色相同,而另一个文本对象将继续为红色。
如果为颜色使用可变类类型,则该类型的每个变量将标识一个包含RGB三元组的对象。如果多个变量标识同一个对象,则使用这些变量中的任何一个来更改对象的属性将有效地更改所有变量的该属性。如果唯一标识该对象的变量是应该更改的变量(与上面的文本对象一样),这可能会很好,但是如果标识该对象的某些变量应该独立于它(例如,如果文本对象识别出与背景相同的颜色对象。)
使用不可变类类型可以使语义更清晰(对不可变对象的引用可以被视为简单地封装其内容)但是只要有必要改变某些东西的颜色就必须使用找到或创建一个封装正确颜色的颜色对象;这比简单地更新存储的号码要多得多。
除非您需要在颜色之间建立关系,否则我建议使用整数来表示颜色。它相对有效,而且语义很简单。用于表示颜色的整数类型的每个不同存储位置将彼此独立,并且它们之间不会发生不需要的交互。
答案 6 :(得分:0)
Doorknob将byte
存储在int
中的想法很好,但他的实施并不适用于我。我最终使用了Color
class内置的Java。例如,要创建表示颜色的int
的数据结构,您可以执行以下操作:
Color c = new Color(55, 155, 255);
int ci = c.getRGB();
稍后,如果您要解码int
:
Color cd = new Color(ci);
int R = cd.getRed(); // 55