如何通过多线程 Java 编程最大限度地利用资源(RAM 和 CPU)?

Posted

技术标签:

【中文标题】如何通过多线程 Java 编程最大限度地利用资源(RAM 和 CPU)?【英文标题】:how to maximize resource (RAM and CPU) usage with multi-threaded java programming? 【发布时间】:2013-08-20 16:31:54 【问题描述】:

我的代码在具有 128 GB RAM 和 64 个内核的 Windows 2008 Server(64 位)上的 32 位 JVM (JRE v1.6) 上运行。但是,我可以指定的最大堆空间是 1.5 GB。我的代码如下所示。

int numThreads = Runtime.getRuntime.availableProcessors();
List<Callable<Long>> tasks = new ArrayList<Callable<Long>>();
File dir = new File("/path/to/data");
File[] dataFiles = dir.listFiles();
for(File dataFile : dataFiles) 
 MyTask task = new MyTask(dataFile);
 tasks.add(task);

ExecutorService executor = Executors.newFixedThreadPoll(numThreads);
List<Future<Long>> results = executor.invokeAll(tasks);
long total = 0L;
for(Future<Long> result : results) 
 total += result.get();

System.out.println("total = " + total);
executor.shutdown();

此代码引发 OutOfMemoryError。我所做的是将线程数更改为更小。

int numThreads = Runtime.getRuntime.availableProcessors();
if(numThreads < 1 || numThreads > 4) 
 numThreads = 4;

这个修改后的代码还没有抛出 OutOfMemoryError,但是,这让我很失望,因为有很多资源(RAM 和 CPU 资源)没有被使用。如何在我的环境中最大限度地利用资源?

最重要的是,我想要一些关于 1.5 GB 最大堆空间限制的解决方法的反馈。请注意, Callable 任务是令人尴尬的并行。

我考虑过创建一个 DOS bat 文件来遍历我的输入文件,然后简单地调用

java -cp %CP% -Xms1024m -Xmx1536m net.analysis.MyProg %1

但这似乎有点古怪/笨拙(现在我必须在 DOS bat 中有逻辑来确定要创建多少进程,并等待这些进程完成后再生成新进程)。

感谢任何帮助。

【问题讨论】:

java 1.6 32-bit min and max heap memory issue 的可能重复项 这个问题和你上一个问题的答案一样。我觉得你缺少一些东西,但我不知道为什么。 @PeterLawrey 它不完全相同,但它是相关的。我打算在那个线程上发帖,但我已经将该线程标记为已回答。这个线程处理“解决方法”,而前一个线程处理理解内存限制。 我能想到的所有解决方法都涉及安装 64 位版本的 Java,这也是解决方案。如果我是系统管理员并且我购买了 128 GB 的机器,我会坚持使用 64 位应用程序,否则这会使机器变得毫无意义。如果您的 IT 不明白这一点,那么您就有一个非常基本的问题。 【参考方案1】:

选项:

    切换到 64 位 JVM。 运行一大堆 32 位 JVM,每个 JVM 都执行必须完成的工作的子集。

【讨论】:

ProcessBuilder 会有帮助吗?我正在考虑一个 DOS bat 文件,但我想尽可能地保留 Java 中的所有内容。当我使用 ProcessBuilder 时,这会创建一堆 32 位 JVM,每个 JVM 都有自己的 1.5 GB 最大堆,对吧? 当然,ProcessBuilder 可以创建每个将完成工作的进程。这使您可以在每个流程之间都有一个流 - 使用这些流将工作传递给流程并返回结果。因此,您将拥有一个管理所有工作和流程的“驱动程序”应用程序和一个执行任务并返回结果的“工作人员”应用程序。【参考方案2】:

32 位 JVM 的最大堆空间约为 1.5GB。当然,您必须切换到在 64 位操作系统上运行的 64 位 JVM 才能分配更多。这是 32 位 JVM 使用 32 位地址这一事实的直接结果。 64 位 JVM 可以访问的堆空间大约是 32 位 JVM 的 2 到 40 亿倍。

【讨论】:

是的,我知道。但请注意,在这种环境下改变任何事情都是与官僚地雷的政治艰苦战斗。 没有其他选项可以增加 JVM 可用的堆内存。这是 32 位架构的寻址限制。这就是存在 64 位架构的原因。 @JaneWayne 那么这是一个政治问题 - 在你的情况下,没有一个可以被技术规避。由于限制,接受它的糟糕运行 - 只需向责任方指出问题并解释解决方案。然后它就变成了他们的问题,每次有人抱怨软件的性能时,都会将他们指向(记录在案的)问题。如果这不能给你带来结果......换个公司,认真的。 @JaneWayne 所以你的问题真的是政治问题,而不是技术问题,所以我们无法帮助你。您无法更改模拟 32 位程序的方式,如果可以的话,您也不想这样做。 @Durandal 所以没有技术解决方法吗?我能想到一些(例如 bat 脚本),但我想知道是否还有其他人有更好的想法和/或面临类似的限制并有解决方案。【参考方案3】:

为什么不考虑 newCachedThreadPool()。我认为它应该非常适合您的要求和约束。 IT 创建一个线程池,根据需要创建新线程,但在可用时将重用以前构建的线程。这些池通常会提高执行许多短期异步任务的程序的性能。执行调用将重用以前构造的 线程(如果可用)。如果没有可用的现有线程,将创建一个新线程并将其添加到池中。未使用 60 秒 的线程将被终止并从 缓存。因此,闲置时间足够长的池不会消耗任何资源

查看 api 文档了解更多信息

【讨论】:

以上是关于如何通过多线程 Java 编程最大限度地利用资源(RAM 和 CPU)?的主要内容,如果未能解决你的问题,请参考以下文章

Java并发并发编程的挑战

多线程上下文切换

初识并发编程

多线程上下文切换优化与注意

JAVA并发编程艺术 一(并发编程的挑战)

Java并发机制及锁的实现原理