Java:线程本地内存分配和可伸缩性
Java: Thread local memory allocation and scalability
我正在尝试提高 java 应用程序的可伸缩性,该应用程序在单独的线程中处理许多小任务。但它显示出意想不到的糟糕结果。似乎内存分配根本不可调用。这很奇怪,因为对象是为每个线程本地分配的。 Java 内存管理器应该能够在没有全局锁定的情况下在线程的本地堆中分配它们。 GC 线程没有显示任何重要 activity。
有一个简单的测试:
private static class AllocTest implements Runnable
{
@Override
public void run()
{
for (int i = 0; i < 100000; ++i)
{
char[] s = new char[100];
}
}
}
final int THREADS_COUNT = 4;
LinkedBlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>(THREADS_COUNT);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_COUNT, THREADS_COUNT, 1, TimeUnit.HOURS, taskQueue, threadFactory);
pool.prestartAllCoreThreads();
long startTime = System.nanoTime();
for (int i = 0; i < 1000; ++i)
{
pool.getQueue().offer(new AllocTest(), 1, TimeUnit.MINUTES);
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.HOURS);
long endTime = System.nanoTime();
System.out.print("Elapsed time: ");
System.out.print(TimeUnit.NANOSECONDS.toMillis(endTime - startTime));
将线程数 (THREADS_COUNT) 从 1 更改为 4,我得到几乎相同的结果:
THREADS_COUNT时间
- 4811
- 4783
- 4814
- 4823
测试 运行 使用“-server”VM 标志。
Java 版本 - 1.8.0_66。
在平台上试用 - Windows 7 x64(1 个处理器,8 个内核),SunOS 5.10 x64(2 个处理器,8 个内核)。
对于此类行为的任何解释或关于如何获得更好的可伸缩性(例如某些特定的 jvm 设置)的建议,我将不胜感激。
编辑:我知道引入一些线程本地池来重用对象而不是每次都分配它应该会提高可伸缩性。但这只是解决方法(我目前正在尝试实施)。我实际上想找到这两个问题的答案:1)为什么提供的测试不可扩展?和 2) 如果不是硬件限制,如何在不更改代码的情况下使 jvm 高效工作?
看起来分配速度非常接近我的 RAM 的理论最大值。 Java 即使在 1 个线程中也达到了它,所以它并没有用更多的线程更好。此外,我发现我的 PC 配置为在单通道模式下使用内存(两个 RAM 模块安装到不同颜色的插槽中)。在双模式下,我观察到两倍更好的结果。
P.S。谢谢大家的想法!
我正在尝试提高 java 应用程序的可伸缩性,该应用程序在单独的线程中处理许多小任务。但它显示出意想不到的糟糕结果。似乎内存分配根本不可调用。这很奇怪,因为对象是为每个线程本地分配的。 Java 内存管理器应该能够在没有全局锁定的情况下在线程的本地堆中分配它们。 GC 线程没有显示任何重要 activity。 有一个简单的测试:
private static class AllocTest implements Runnable
{
@Override
public void run()
{
for (int i = 0; i < 100000; ++i)
{
char[] s = new char[100];
}
}
}
final int THREADS_COUNT = 4;
LinkedBlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>(THREADS_COUNT);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_COUNT, THREADS_COUNT, 1, TimeUnit.HOURS, taskQueue, threadFactory);
pool.prestartAllCoreThreads();
long startTime = System.nanoTime();
for (int i = 0; i < 1000; ++i)
{
pool.getQueue().offer(new AllocTest(), 1, TimeUnit.MINUTES);
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.HOURS);
long endTime = System.nanoTime();
System.out.print("Elapsed time: ");
System.out.print(TimeUnit.NANOSECONDS.toMillis(endTime - startTime));
将线程数 (THREADS_COUNT) 从 1 更改为 4,我得到几乎相同的结果: THREADS_COUNT时间
- 4811
- 4783
- 4814
- 4823
测试 运行 使用“-server”VM 标志。 Java 版本 - 1.8.0_66。 在平台上试用 - Windows 7 x64(1 个处理器,8 个内核),SunOS 5.10 x64(2 个处理器,8 个内核)。
对于此类行为的任何解释或关于如何获得更好的可伸缩性(例如某些特定的 jvm 设置)的建议,我将不胜感激。
编辑:我知道引入一些线程本地池来重用对象而不是每次都分配它应该会提高可伸缩性。但这只是解决方法(我目前正在尝试实施)。我实际上想找到这两个问题的答案:1)为什么提供的测试不可扩展?和 2) 如果不是硬件限制,如何在不更改代码的情况下使 jvm 高效工作?
看起来分配速度非常接近我的 RAM 的理论最大值。 Java 即使在 1 个线程中也达到了它,所以它并没有用更多的线程更好。此外,我发现我的 PC 配置为在单通道模式下使用内存(两个 RAM 模块安装到不同颜色的插槽中)。在双模式下,我观察到两倍更好的结果。
P.S。谢谢大家的想法!