Java线程内存爆炸

3

我对并发编程比较新,正在学习。

我正在使用Java JDK 7(Fork Join API)实现快速排序来对对象列表(100K)进行排序。

在不使用并发的情况下使用这个递归代码片段时,我观察到没有记忆爆炸,一切都很好。

我只是添加了代码,以便在多个核心上使用它(通过扩展类RecursiveAction),然后内存使用量急剧增加,直到达到其限制。通过一些分析,我观察到线程的高创建率,我认为这是可以预料的。但是,一个Java线程本身是否需要更多的内存,或者我在这里漏掉了什么?

快速排序必须需要很多线程,但不会比常规对象多。

当我满足阈值时停止创建RecursiveAction线程,然后切换到顺序代码(不再有线程)吗?

非常感谢。


你根本不应该自己创建线程——应该把这个任务交给 FJ 库。 - Tom Hawtin - tackline
你好Tom。请纠正我如果我错了,但当我们操作FJ API时,我们必须在某些时候使用线程,对吗?虽然我现在这样做可能对生产中的软件来说是灾难性的,但我现在这样做是为了学习并发是如何工作的。 - charpentier damien
4个回答

4

默认情况下,Java线程通常单独占用256k/512k(取决于操作系统、jdk版本等)的堆栈空间。

如果在进行快速排序等CPU密集型进程时运行的线程数超过处理器/核心数量,则会浪费大量资源和速度。因此,请尽量不要运行超过您拥有的核心数的线程。


太快了!非常感谢您的提示! - charpentier damien

0

是的,当工作单元在大约10,000-100,000个操作范围内时,切换到顺序代码是一个好主意。这只是一个经验法则。因此,对于快速排序,当要排序的大小小于10-20,000个元素时,我会转换为顺序执行,具体取决于比较操作的复杂性。

ForkJoinPool的大小是多少 - 通常设置为创建与处理器相同数量的线程,因此您不应该看到太多线程。如果您手动将并行度设置得很高(例如数百或数千),则会看到高(虚拟)内存使用率,因为每个线程都会为堆栈分配空间(默认情况下,在32位Windows和Linux上为256K)。


嗨 mdma,我正在玩一下 ForkJoinPool。我会回来带更多的输出。谢谢。 - charpentier damien

0

对于 CPU 密集型计算,一般来说,一旦线程数超过可用核心数,增加更多线程并不能提高速度。事实上,由于创建线程的开销、每个线程占用的资源(例如线程堆栈)以及同步的成本,它可能会减慢速度。

事实上,即使您拥有无限数量的核心,也不值得创建线程来执行小任务。即使使用线程池和其他巧妙的技巧,如果任务中要完成的工作量太小,使用线程的开销将超过任何节省的时间。(很难准确预测该阈值在哪里,这当然也取决于任务的性质以及与平台相关的因素。)


我遇到了和你一样的问题。谢谢。问题在于,并行化和线程控制比我想象的要困难得多。确实,使用fork join,在一个线程中我可以创建两个子线程(每个子列表围绕着中心点)。其中一个可以很快完成,但是父线程直到第二个子线程也完成其工作(join()方法)后才能完成。这种方案会反复出现。通过将ForkJoinPool中的线程数限制为两个,我最终会得到一个仍在运行的核心,因为另一个核心可以非常快地完成任务。我必须重新考虑这个问题。 - charpentier damien

0

我修改了我的代码,目前结果更好了。我在ForkJoinPool中调用主线程任务,在线程中,如果活动线程比ForkJoinPool中可用的核心数多得多,则不创建更多线程。

我不通过join()方法进行同步。因此,父线程将在创建其子线程后立即死亡。在调用根任务的主函数中,我等待任务完成,也就是没有更多活动线程了。似乎它运行良好,因为内存保持正常,并且我在执行相同的代码时节省了很多时间。

我将继续学习。

谢谢大家!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接