Java虚拟机上的阵列分配和访问与内存争用
遵循以下线程子类的定义(为了方便起见,问题末尾包含了整个可运行Java源文件): 案例1:Java虚拟机上的阵列分配和访问与内存争用,java,arrays,memory,memory-management,concurrency,Java,Arrays,Memory,Memory Management,Concurrency,遵循以下线程子类的定义(为了方便起见,问题末尾包含了整个可运行Java源文件): 案例1:1,2,4,8线程的运行时间,顺序为(7次重复): 我的想法是,非线性缩放是由于内存争用。顺便说一句,早期迭代实际上做得更好——这可能是因为在不同的迭代中,数组被分配到不同的内存区域 案例2:接下来,我在线程的run方法中注释Foo[]arr=array行,并在run方法本身中分配一个新数组:Foo[]arr=new Foo[1024]。测量: >>> All running times:
1,2,4,8
线程的运行时间,顺序为(7次重复):
我的想法是,非线性缩放是由于内存争用。顺便说一句,早期迭代实际上做得更好——这可能是因为在不同的迭代中,数组被分配到不同的内存区域
案例2:接下来,我在线程的run
方法中注释Foo[]arr=array
行,并在run
方法本身中分配一个新数组:Foo[]arr=new Foo[1024]
。测量:
>>> All running times: [2053, 1966, 2089, 1937, 2046, 1909, 2011]
>>> All running times: [1048, 1178, 1100, 1194, 1367, 1271, 1207]
>>> All running times: [578, 508, 589, 571, 617, 643, 645]
>>> All running times: [330, 299, 300, 322, 331, 324, 575]
这一次,一切的规模几乎和预期的一样。我没有想到分配数组的位置会起到任何作用,但很明显,它确实起到了作用。我的想法是,以前分配的阵列彼此非常接近,以至于开始发生内存争用
案例3:为了验证这一假设,我再次取消了对行Foo[]arr=array
的注释,但这次将array
字段初始化为new Foo[32000]
,以确保写入的内存中的位置彼此之间足够远。因此,这里我们再次使用在创建thread对象期间分配的数组,与CASE1的区别只是数组更大
>>> All running times: [2113, 1983, 2430, 2485, 2333, 2359, 2463]
>>> All running times: [1172, 1106, 1163, 1181, 1142, 1169, 1188]
>>> All running times: [578, 677, 614, 604, 583, 637, 597]
>>> All running times: [343, 327, 320, 330, 353, 320, 320]
因此,内存争用似乎是造成这种情况的原因
平台信息:
Ubuntu Server 10.04.3 LTS
8 core Intel(R) Xeon(R) CPU X5355 @2.66GHz
~20GB ram
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02, mixed mode)
问题:这显然是内存争用问题。但为什么会这样
run
方法中创建时,整个数组都在堆栈上分配?此运行时优化的确切条件是什么?肯定没有在堆栈上为100万个元素分配数组吗整个可运行Java程序:
import java.util.ArrayList;
class MultiStackJavaExperiment {
final class Foo {
int x = 0;
}
final class Worker extends Thread {
Foo[] array = new Foo[1024];
int sz;
public Worker(int _sz) {
sz = _sz;
}
public void run() {
Foo[] arr = new Foo[1024];
//Foo[] arr = array;
loop(arr);
}
public void loop(Foo[] arr) {
int i = 0;
int pos = 512;
Foo v = new Foo();
while (i < sz) {
if (i % 2 == 0) {
arr[pos] = v;
pos += 1;
} else {
pos -= 1;
v = arr[pos];
}
i++;
}
}
}
public static void main(String[] args) {
(new MultiStackJavaExperiment()).mainMethod(args);
}
int size = Integer.parseInt(System.getProperty("size"));
int par = Integer.parseInt(System.getProperty("par"));
public void mainMethod(String[] args) {
int times = 0;
if (args.length == 0) times = 1;
else times = Integer.parseInt(args[0]);
ArrayList < Long > measurements = new ArrayList < Long > ();
for (int i = 0; i < times; i++) {
long start = System.currentTimeMillis();
run();
long end = System.currentTimeMillis();
long time = (end - start);
System.out.println(i + ") Running time: " + time + " ms");
measurements.add(time);
}
System.out.println(">>>");
System.out.println(">>> All running times: " + measurements);
System.out.println(">>>");
}
public void run() {
int sz = size / par;
ArrayList < Thread > threads = new ArrayList < Thread > ();
for (int i = 0; i < par; i++) {
threads.add(new Worker(sz));
threads.get(i).start();
}
for (int i = 0; i < par; i++) {
try {
threads.get(i).join();
} catch (Exception e) {}
}
}
}
import java.util.ArrayList;
课堂教学实验{
最后一节课Foo{
int x=0;
}
最后一个类辅助程序扩展线程{
Foo[]数组=新Foo[1024];
int sz;
公共工作者(内华达州){
sz=_sz;
}
公开募捐{
Foo[]arr=新Foo[1024];
//Foo[]arr=数组;
环路(arr);
}
公共无效循环(Foo[]arr){
int i=0;
int pos=512;
Foo v=新的Foo();
而(i测量值=新的ArrayList();
for(int i=0;i>>”);
System.out.println(“>>>所有运行时间:“+测量值”);
System.out.println(“>>>”);
}
公开募捐{
INSZ =大小/PAR;
ArrayListthreads=新的ArrayList();
对于(int i=0;i<PAR;i++){
添加(新工作线程(sz));
threads.get(i.start();
}
对于(int i=0;i<PAR;i++){
试一试{
threads.get(i.join();
}捕获(例外e){}
}
}
}
我认为你需要减少你的代码,这样它就不会做很多可能会让人困惑的事情。在减少代码之后,我很清楚,您每次只访问相同的数组位置。i、 e.位置512
如果你最小化了你的代码,重用你的线程,这样你就不会停止/启动它们,你会得到更多可重复的结果
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MultiStackJavaExperiment {
static final int size = Integer.getInteger("size", 500000000);
public static void main(String... args) throws ExecutionException, InterruptedException {
int par = 8;
for (int s = 64; s <= 64 * 1024; s *= 2) {
int times = args.length == 0 ? 1 : Integer.parseInt(args[0]);
long[] measurements = new long[times];
ExecutorService es = Executors.newFixedThreadPool(par);
List<Future<?>> futures = new ArrayList<Future<?>>(times);
for (int i = 0; i < times; i++) {
long start = System.currentTimeMillis();
final int sz = size / par;
futures.clear();
for (int j = 0; j < par; j++) {
final Object[] arr = new Object[s];
futures.add(es.submit(new Runnable() {
@Override
public void run() {
final int bits = 7, arraySize = 1 << bits;
int i = 0;
int pos = 32;
Object v = new Object();
while (i < sz) {
if (i % 2 == 0) {
arr[pos] = v;
pos += 1;
} else {
pos -= 1;
v = arr[pos];
}
i++;
}
}
}));
}
for (Future<?> future : futures)
future.get();
long time = System.currentTimeMillis() - start;
// System.out.println(i + ") Running time: " + time + " ms");
measurements[i] = time;
}
es.shutdown();
System.out.println("par = " + par + " arr.length= "+ s + " >>> All running times: " + Arrays.toString(measurements));
}
}
}
如果将数组移动到线程内,则会得到
par = 8 arr.length= 64 >>> All running times: [225, 151, 151, 150, 152, 153, 152]
par = 8 arr.length= 256 >>> All running times: [155, 151, 151, 151, 151, 151, 155]
par = 8 arr.length= 1024 >>> All running times: [153, 152, 151, 151, 151, 155, 152]
par = 8 arr.length= 4096 >>> All running times: [155, 156, 151, 152, 151, 155, 155]
par = 8 arr.length= 16384 >>> All running times: [154, 157, 152, 152, 158, 153, 153]
par = 8 arr.length= 65536 >>> All running times: [155, 157, 152, 184, 181, 154, 153]
par = 8 arr.length= 262144 >>> All running times: [240, 159, 166, 151, 172, 154, 160]
par = 8 arr.length= 1048576 >>> All running times: [165, 162, 163, 162, 163, 162, 163]
关闭tlab w
import java.util.ArrayList;
class MultiStackJavaExperiment {
final class Foo {
int x = 0;
}
final class Worker extends Thread {
Foo[] array = new Foo[1024];
int sz;
public Worker(int _sz) {
sz = _sz;
}
public void run() {
Foo[] arr = new Foo[1024];
//Foo[] arr = array;
loop(arr);
}
public void loop(Foo[] arr) {
int i = 0;
int pos = 512;
Foo v = new Foo();
while (i < sz) {
if (i % 2 == 0) {
arr[pos] = v;
pos += 1;
} else {
pos -= 1;
v = arr[pos];
}
i++;
}
}
}
public static void main(String[] args) {
(new MultiStackJavaExperiment()).mainMethod(args);
}
int size = Integer.parseInt(System.getProperty("size"));
int par = Integer.parseInt(System.getProperty("par"));
public void mainMethod(String[] args) {
int times = 0;
if (args.length == 0) times = 1;
else times = Integer.parseInt(args[0]);
ArrayList < Long > measurements = new ArrayList < Long > ();
for (int i = 0; i < times; i++) {
long start = System.currentTimeMillis();
run();
long end = System.currentTimeMillis();
long time = (end - start);
System.out.println(i + ") Running time: " + time + " ms");
measurements.add(time);
}
System.out.println(">>>");
System.out.println(">>> All running times: " + measurements);
System.out.println(">>>");
}
public void run() {
int sz = size / par;
ArrayList < Thread > threads = new ArrayList < Thread > ();
for (int i = 0; i < par; i++) {
threads.add(new Worker(sz));
threads.get(i).start();
}
for (int i = 0; i < par; i++) {
try {
threads.get(i).join();
} catch (Exception e) {}
}
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MultiStackJavaExperiment {
static final int size = Integer.getInteger("size", 500000000);
public static void main(String... args) throws ExecutionException, InterruptedException {
int par = 8;
for (int s = 64; s <= 64 * 1024; s *= 2) {
int times = args.length == 0 ? 1 : Integer.parseInt(args[0]);
long[] measurements = new long[times];
ExecutorService es = Executors.newFixedThreadPool(par);
List<Future<?>> futures = new ArrayList<Future<?>>(times);
for (int i = 0; i < times; i++) {
long start = System.currentTimeMillis();
final int sz = size / par;
futures.clear();
for (int j = 0; j < par; j++) {
final Object[] arr = new Object[s];
futures.add(es.submit(new Runnable() {
@Override
public void run() {
final int bits = 7, arraySize = 1 << bits;
int i = 0;
int pos = 32;
Object v = new Object();
while (i < sz) {
if (i % 2 == 0) {
arr[pos] = v;
pos += 1;
} else {
pos -= 1;
v = arr[pos];
}
i++;
}
}
}));
}
for (Future<?> future : futures)
future.get();
long time = System.currentTimeMillis() - start;
// System.out.println(i + ") Running time: " + time + " ms");
measurements[i] = time;
}
es.shutdown();
System.out.println("par = " + par + " arr.length= "+ s + " >>> All running times: " + Arrays.toString(measurements));
}
}
}
par = 8 arr.length= 64 >>> All running times: [539, 413, 444, 444, 457, 444, 456]
par = 8 arr.length= 256 >>> All running times: [398, 527, 514, 529, 445, 441, 445]
par = 8 arr.length= 1024 >>> All running times: [419, 507, 477, 422, 412, 452, 396]
par = 8 arr.length= 4096 >>> All running times: [316, 282, 250, 232, 242, 229, 238]
par = 8 arr.length= 16384 >>> All running times: [316, 207, 209, 212, 208, 208, 208]
par = 8 arr.length= 65536 >>> All running times: [211, 211, 208, 208, 208, 291, 206]
par = 8 arr.length= 262144 >>> All running times: [366, 210, 210, 210, 210, 209, 211]
par = 8 arr.length= 1048576 >>> All running times: [296, 211, 215, 216, 213, 211, 211]
par = 8 arr.length= 64 >>> All running times: [225, 151, 151, 150, 152, 153, 152]
par = 8 arr.length= 256 >>> All running times: [155, 151, 151, 151, 151, 151, 155]
par = 8 arr.length= 1024 >>> All running times: [153, 152, 151, 151, 151, 155, 152]
par = 8 arr.length= 4096 >>> All running times: [155, 156, 151, 152, 151, 155, 155]
par = 8 arr.length= 16384 >>> All running times: [154, 157, 152, 152, 158, 153, 153]
par = 8 arr.length= 65536 >>> All running times: [155, 157, 152, 184, 181, 154, 153]
par = 8 arr.length= 262144 >>> All running times: [240, 159, 166, 151, 172, 154, 160]
par = 8 arr.length= 1048576 >>> All running times: [165, 162, 163, 162, 163, 162, 163]
par = 8 arr.length= 64 >>> All running times: [608, 467, 467, 457, 468, 461, 428]
par = 8 arr.length= 256 >>> All running times: [437, 437, 522, 512, 522, 369, 535]
par = 8 arr.length= 1024 >>> All running times: [394, 395, 475, 525, 470, 440, 478]
par = 8 arr.length= 4096 >>> All running times: [347, 215, 238, 226, 236, 204, 271]
par = 8 arr.length= 16384 >>> All running times: [291, 157, 178, 151, 150, 151, 152]
par = 8 arr.length= 65536 >>> All running times: [163, 152, 162, 151, 159, 159, 154]
par = 8 arr.length= 262144 >>> All running times: [164, 172, 152, 169, 160, 161, 160]
par = 8 arr.length= 1048576 >>> All running times: [295, 153, 164, 153, 166, 154, 163]