Java FixedThreadPool和ExecutorCompletionService的OutOfMemory错误
我正在使用应用程序what,它应该从数据库中获取用户列表,并从目录(ldap或AD)中更新这些详细信息。我在多核机器上做了什么,所以我创建了这个应用程序(代码如下)。我正在使用CompletionService并在Future对象中获得结果 过了一段时间,我收到了“无法创建新的本机线程”消息的内存不足错误。在TaskManager中,我看到应用程序创建了大量线程,但我要求创建大小等于处理器数量的固定线程池 我的代码有什么问题Java FixedThreadPool和ExecutorCompletionService的OutOfMemory错误,java,concurrency,out-of-memory,executorservice,completion-service,Java,Concurrency,Out Of Memory,Executorservice,Completion Service,我正在使用应用程序what,它应该从数据库中获取用户列表,并从目录(ldap或AD)中更新这些详细信息。我在多核机器上做了什么,所以我创建了这个应用程序(代码如下)。我正在使用CompletionService并在Future对象中获得结果 过了一段时间,我收到了“无法创建新的本机线程”消息的内存不足错误。在TaskManager中,我看到应用程序创建了大量线程,但我要求创建大小等于处理器数量的固定线程池 我的代码有什么问题 class CheckGroupMembership { public
class CheckGroupMembership {
public static void main(String[] args) throws Exception {
final ExecutorService executor = Executors.newFixedThreadPool(**Runtime.getRuntime().availableProcessors()**);
CompletionService<LdapPerson> completionService =
new ExecutorCompletionService(executor)<LdapPerson>(executor);
final int limit = 2000;
DocumentService service1 = new DocumentService();
List<String> userNamesList = service1.getUsersListFromDB(limit);
List<LdapPerson> ldapPersonList = new ArrayList() <LdapPerson> (userNamesList.size());
LdapPerson person;
for (String userName : userNamesList) {
completionService.submit(new GetUsersDLTask(userName));
}
try {
for (int i = 0, n = userNamesList.size(); i < n; i++) {
Future<LdapPerson> f = completionService.take();
person = f.get();
ldapPersonList.add(person);
}
} catch (InterruptedException e) {
System.out.println("InterruptedException error:" + e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.exit(0);
}
}
类CheckGroupMembership{
公共静态void main(字符串[]args)引发异常{
final ExecutorService executor=Executors.newFixedThreadPool(**Runtime.getRuntime().availableProcessors()**);
CompletionService CompletionService=
新执行人完成服务(执行人)(执行人);
最终整数限值=2000;
DocumentService service1=新的DocumentService();
List userNamesList=service1.getUsersListFromDB(限制);
List ldapPersonList=newarraylist()(userNamesList.size());
第一人称;
for(字符串用户名:userNamesList){
submit(新的GetUsersDLTask(用户名));
}
试一试{
对于(int i=0,n=userNamesList.size();i
错误CheckGroupMembership:85-java.lang.OutOfMemoryError:无法创建新的本机线程
java.util.concurrent.ExecutionException:java.lang.OutOfMemoryError:无法创建新的本机线程
位于java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
位于java.util.concurrent.FutureTask.get(FutureTask.java:83
GetuserDLs任务
public class GetUsersDLTask implements Callable<LdapPerson> {
private String userName;
public GetUsersDLTask(String u) {
this.userName = u;
}
@Override
public LdapPerson call() throws Exception {
LdapService service = new LdapService();
return service.getUsersDLs(userName);
}
}
公共类GetUsersDLTask实现可调用{
私有字符串用户名;
公共GetUsersDLTask(字符串u){
this.userName=u;
}
@凌驾
public LdapPerson call()引发异常{
LdapService=new LdapService();
return service.getUsersDLs(用户名);
}
}
Executors.newFixedThreadPool将接受许多任务的提交,但只执行您允许的线程数。因此,如果您有一个固定的2个线程池,但您提交了50个任务,则其他48个任务将在执行器内部排队,并在执行线程完成任务时运行。似乎需要限制在代码中生成的线程数
编辑:签出)您是否验证了在固定池中创建的线程数。也许可用处理器的数量太大了。我很难相信您没有在
GetUsersDLTask
中创建线程(或者至少是它的服务对象)。如果查看stacktrace,将从Future的get()
方法引发异常。设置此异常的唯一方法是在执行器调用callable.call()
之后。在call()
方法中出现的任何可丢弃项都将在将来的内部异常
字段中设置
例如:
Thread Pool:
Thread-1
invoke call()
call()
Create Thread
throw OutOfMemoryError
propogate error to Thread pool
set exception
否则,当您将ask提交到线程池时会发生此异常,而不是当您从未来获得时。从JVM请求线程转储。线程的名称是什么?LdapService是否创建线程?LdapService不创建任何线程。我使用的是standarjavax.naming.*Stuff OOM之前JVM的线程转储中有什么?线程名是什么?线程名是Thread-2和Thread-3,但executor当时是否实例化了其余48个线程?我如何控制线程生成?正如建议的编辑所指出的,您提交任务,线程池管理线程。嗯,为什么投票反对?内存不足,因为生成的线程太多-执行器将根据您提供的线程数执行,但只执行固定池中提供的线程数。您可以控制线程生成,因为您是向执行器提交线程的人;你真的需要一个新的线程为每个用户名?也许把他们分成100个左右的小组,然后在一个线程中提交。问题和评论是两个不同的想法。不用担心:)@sqrv这不是真的。提交Callable/Runnable时,它将被放置在ES工作队列中。如果线程池是固定的,那么每个Runnable/Callable将仅在线程可用且可以轮询队列时使用。如果您有一个
Executors.newCachedThreadPool
,那么是的,如果线程没有空闲,则每个提交的可调用线程都会生成一个新线程。不是这样的是的,我相信你是对的。在GetUsersDLTask中,我创建了LdapService的新实例,它每次都创建InitialContext对象(它是创建新线程,用于与ldap的套接字通信)。所以你对mnemocode的假设是正确的。谢谢你的建议!