これがJavaコンストラクターと静的イニシャルアイザーの65,535バイトの制限を超えるのはなぜですか?
質問
免責事項:Javaで実行時にこれを生成できることに気付きました。これは、パフォーマンステスト中に非常に特別なケースに必要でした。私は別のアプローチを見つけたので、今ではこれは実用的なものよりも好奇心の大きさです。
以下を静的フィールドとして、インスタンスフィールドとして試し、コンストラクター内で直接初期化しました。 Eclipseが「コンストラクターTestData()のコードが65535バイト制限を超えている」または「静的イニシャルのコードが65535バイトの制限を超えている」かのいずれかのいずれかを通知するたびに。
整数は10,000個です。各INTが4バイト(32ビット)の場合、それは40,000バイトではありませんか?単にアレイを構築するだけでなく、実際に25,0000バイトのオーバーヘッドがありますか?
データは、この小さなPythonで生成されます。
#!/usr/bin/python
import random;
print "public final int[] RANDOM_INTEGERS = new int[] {";
for i in range(1,10000):
print str(int(random.uniform(0,0x7fffffff))) + ",";
print "};";
これが小さなサンプルです:
public final int[] RANDOM_INTEGERS = new int[] {
963056418, 460816633, 1426956928, 1836901854, 334443802, 721185237, 488810483,
1734703787, 1858674527, 112552804, 1467830977, 1533524842, 1140643114, 1452361499,
716999590, 652029167, 1448309605, 1111915190, 1032718128, 1194366355, 112834025,
419247979, 944166634, 205228045, 1920916263, 1102820742, 1504720637, 757008315,
67604636, 1686232265, 597601176, 1090143513, 205960256, 1611222388, 1997832237,
1429883982, 1693885243, 1987916675, 159802771, 1092244159, 1224816153, 1675311441,
1873372604, 1787757434, 1347615328, 1868311855, 1401477617, 508641277, 1352501377,
1442984254, 1468392589, 1059757519, 1898445041, 1368044543, 513517087, 99625132,
1291863875, 654253390, 169170318, 2117466849, 1711924068, 564675178, 208741732,
1095240821, 1993892374, 87422510, 1651783681, 1536657700, 1039420228, 674134447,
1083424612, 2137469237, 1294104182, 964677542, 1506442822, 1521039575, 64073383,
929517073, 206993014, 466196357, 1139633501, 1692533218, 1934476545, 2066226407,
550646675, 624977767, 1494512072, 1230119126, 1956454185, 1321128794, 2099617717,
//.... to 10,0000 instances
解決
{1000001、1000002、1000003}で配列を初期化するためのバイトコードは次のとおりです。
5 iconst_3
6 newarray int [10]
8 dup
9 iconst_0
10 ldc <Integer 1000001> [12]
12 iastore
13 dup
14 iconst_1
15 ldc <Integer 1000002> [13]
17 iastore
18 dup
19 iconst_2
20 ldc <Integer 1000003> [14]
22 iastore
23 putfield net.jstuber.test.TestArrayInitializingConstructor.data : int[] [15]
したがって、この小さな配列の場合、各要素には5バイトのJavaバイトコードが必要です。より大きな配列の場合、配列インデックスと定数プールへのインデックスの両方が、ほとんどの要素で3バイトを使用し、配列ごとに8バイトにつながります。したがって、10000の要素については、約80kbのバイトコードを期待する必要があります。
16のビットインデックスでビッグ配列を初期化するためのコードは次のようになります。
2016 dup
2017 sipush 298
2020 ldc_w <Integer 100298> [310]
2023 iastore
2024 dup
2025 sipush 299
2028 ldc_w <Integer 100299> [311]
他のヒント
配列リテラルはバイトコードに変換され、アレイに値が記入されるため、各数値にさらにいくつかのバイトが必要です。
そのデータを静的初期イザーブロックでクラスロード時間にロードするリソースに移動してみませんか?これは、使用することで簡単に実行できます MyClass.class.getClassLoader().getResourceAsStream()
. 。とにかく、それが属する場所のようです。
またはさらに良いことに、利用可能なJavaツールを使用して、静的イニシャルブロックにランダム値を作成します。そして、繰り返し可能な「ランダム」番号が必要な場合は、 Random
インスタンスは、毎回固定されているがランダムに選択された番号を使用します。
整数の値に加えて、コンストラクターと初期化は、整数を配列にロードするためのJVM命令を含める必要があります。
はるかに簡単で実用的なアプローチは、バイナリ形式またはテキストとして、数値をファイルに保存することです。
Javaがこのようにアレイを初期化するものはわかりませんが、大きな配列を効率的に初期化しません。
文字のコードサイズは65535を超えると思います。10000整数で取られたメモリではありません。
これが、それらのINTをアルファナムで表すために必要なメモリの量である可能性があると思います。この制限はコード自体に適用される可能性があると思うので、たとえば、各INT:1494512072は、INT32に使用される4バイトのみではなく、実際には10バイト(1桁あたり1桁)を取ります。