我创建了自己的小线程安全(确认它使用了很多测试)类似于Looper的库和Android for Java中的Handler以及其他一些扩展。一切都很完美,表现相当不错(通过剖析验证)。它基本上做的是每个线程都有自己的队列,队列有一个句柄。使用此处理程序,每个其他线程可以在队列中放置runnable或callable。
所以只是为了检查它在重负载下的表现,我做了一个小程序,我生成1000个线程,每个线程在所有其他线程(即999个其他线程)中发布10个Runnables,然后最终每个线程都对每个其他线程发出关闭请求线。如果我为< 500线程执行它,这可以正常工作。一旦我有超过700,程序永远不会结束,最终有一个OutOfMemoryError。当我描述它时,我得到了以下结果:
用于随时间释放内存的内存图
以下是日志:
-8.035:[GC 170956K-> 2635K(517952K),0.0063501 secs]
-8.610:[GC 170955K-> 15507K(512832K),0.1745043 secs]
9.543:[GC 175507K-> 36673K(516928K),0.3060842 secs]
8.610:[GC 170955K-> 15507K(512832K),0.1745043 secs]
9.543:[GC 175507K-> 36673K(516928K),0.3060842 secs]
10.321:[GC 196673K-> 54009K(488896K),0.3298292秒]
11.431:[GC 185977K-> 84189K(502912K),0.2965050 secs]
12.818:[GC 216157K-> 111017K(458752K),0.4901188 secs]
16.660:[GC 198825K-> 144073K(470528K),0.7027064 secs]
17.616:[GC 231881K-> 156962K(479424K),0.3269871 secs]
18.407:[GC 247490K-> 177262K(483584K),0.2446838 secs]
18.924:[GC 268206K-> 194510K(481216K),0.2892096 secs]
19.446:[GC 285454K-> 210302K(487168K),0.3186975 secs]
20.022:[GC 308734K-> 225446K(485120K),0.2547074 secs]
20.610:[GC 323878K-> 242026K(490624K),0.2302190 secs]
21.109:[GC 348010K-> 260490K(489216K),0.2775054 secs]
21.692:[GC 366474K-> 280570K(491200K),0.3264718 secs]
22.359:[GC 389114K-> 300690K(491200K),0.3274282 secs]
23.097:[GC 409234K-> 321958K(490944K),0.3410703 secs]
23.722:[GC 430374K-> 340846K(491136K),0.3375997 secs]
24.060:[完整GC 340846K-> 303754K(491136K),2.2736636秒]
26.727:[GC 412170K-> 324490K(492096K),0.1968805 secs]
27.235:[GC 434698K-> 345614K(491968K),0.2752622 secs]
27.510:[完整GC 345614K-> 334283K(491968K),2.1151760秒]
29.968:[完整GC 444491K-> 349326K(491968K),2.5176330秒]
32.817:[完整GC 459534K-> 348355K(491968K),3.2688566秒]
36.553:[Full GC 458563K-> 371805K(491968K),2.3835641 secs]
39.211:[完整GC 459776K-> 395739K(491968K),2.2324407秒]
41.654:[完整GC 459776K-> 409135K(491968K),2.2631054秒]
44.113:[完整GC 459776K-> 396769K(491968K),3.4707959秒]
47.930:[完整GC 459775K-> 415668K(491968K),2.9166601秒]
51.051:[完整GC 459775K-> 425117K(491968K),2.6670247秒]
53.886:[完整GC 459775K-> 432457K(491968K),2.2265421秒]
56.192:[完整GC 459775K-> 422948K(491968K),3.2675329秒]
59.651:[完整GC 459775K-> 436339K(491968K),2.3835789秒]
62.136:[完整GC 459775K-> 441349K(491968K),2.2442554秒]
64.433:[完整GC 459775K-> 445241K(491968K),2.2672156秒]
66.750:[完整GC 459775K-> 437517K(491968K),3.2987756秒]
70.109:[完整GC 459776K-> 447665K(491968K),1.9295598秒]
72.069:[完整GC 459776K-> 449837K(491968K),1.8525232秒]
73.966:[完整GC 459776K-> 451969K(491968K),1.9544717秒]
75.956:[完整GC 459776K-> 445178K(491968K),3.3964743秒]
依此类推,直到错误被抛出。 进一步我发现,一旦线程开始终止,#降到167,但其余的从未终止。 不存在任何竞争条件,因为它对于< 500线程表现良好。现在我知道这可能是由于饥饿,但如果饥饿是原因,那么当有1000个线程而不是只有150个线程时就会发生。
这可能是什么原因?
以下是代码的简短摘录:
protected static <T> Future<T> execute(final Double id, Callable<T> call)
{
if(call==null)
throw new NullPointerException("Callable is null");
synchronized(id)
{
if(mapBoolean.get(id))
{
setPollTime(0);
throw new RejectedExecutionException();
}
RunnableFuture<T> ftask = new FutureTask<T>(call);
LinkedBlockingQueue<Runnable> queue = map.get(id);
queue.add(ftask);//System.out.println("added");
return ftask;
}
}
这是执行它的代码
public static void loop() throws InterruptedException
{
LinkedBlockingQueue<Runnable> queue = map.get(tlocal.get());
Random random = new Random();// This is used instead of Math.random() so that
//there is less contention on Math.random(). See the API Documentation of Math.random()
while(!Thread.currentThread().isInterrupted())
{
try{
//Runnable runnable = queue.take(); cannot be used, as we will have to synchronize the whole block for
//atomicity, see @link shutDown() for more info
Runnable runnable = queue.poll(pollTime, TimeUnit.MILLISECONDS);
if(runnable!=null)//if the polled object is not null, if null try again
{
runnable.run();
}
关闭:
synchronized(id)
{
mapBoolean.put(id, !shut);
}
PS:我的是双核机器,2GB内存
PPS:分配的堆大小为512
答案 0 :(得分:3)
基本上你有一个N 2 内存问题 - 1000个线程中的每一个在~1000个线程中生成10个runnable,所以你有10,000,000个runnables。
当你创建一个具有N 2 内存使用的程序时,你不应该惊讶地看到随着你增加N而内存增加。最后你已经到了你所处的阶段基本上是在抨击GC。
请注意,这可以很容易地显示在其他情况下不发生的竞争条件 - 您不应该仅仅因为程序在“简单”条件下工作,这意味着没有代码中的竞争条件。这只是意味着你没有任何非常明显的竞争条件。微妙的竞争条件是最难找到的。
如果更改分配的堆大小以使GC不会崩溃,会发生什么情况,例如:到1.25GB?您可能会发现它的性能要好得多,但有时 还没有完成(例如,只需要几个线程)。