计划没有继续进行

时间:2011-09-09 13:19:51

标签: multithreading garbage-collection thread-safety

我创建了自己的小线程安全(确认它使用了很多测试)类似于Looper的库和Android for Java中的Handler以及其他一些扩展。一切都很完美,表现相当不错(通过剖析验证)。它基本上做的是每个线程都有自己的队列,队列有一个句柄。使用此处理程序,每个其他线程可以在队列中放置runnable或callable。 所以只是为了检查它在重负载下的表现,我做了一个小程序,我生成1000个线程,每个线程在所有其他线程(即999个其他线程)中发布10个Runnables,然后最终每个线程都对每个其他线程发出关闭请求线。如果我为< 500线程执行它,这可以正常工作。一旦我有超过700,程序永远不会结束,最终有一个OutOfMemoryError。当我描述它时,我得到了以下结果: Plot of memory used to memory free with time 用于随时间释放内存的内存图

以下是日志:


-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

1 个答案:

答案 0 :(得分:3)

基本上你有一个N 2 内存问题 - 1000个线程中的每一个在~1000个线程中生成10个runnable,所以你有10,000,000个runnables。

当你创建一个具有N 2 内存使用的程序时,你不应该惊讶地看到随着你增加N而内存增加。最后你已经到了你所处的阶段基本上是在抨击GC。

请注意,这可以很容易地显示在其他情况下发生的竞争条件 - 您不应该仅仅因为程序在“简单”条件下工作,这意味着没有代码中的竞争条件。这只是意味着你没有任何非常明显的竞争条件。微妙的竞争条件是最难找到的。

如果更改分配的堆大小以使GC不会崩溃,会发生什么情况,例如:到1.25GB?您可能会发现它的性能要好得多,但有时 还没有完成(例如,只需要几个线程)。