Java FixedThreadPool和ExecutorCompletionService的OutOfMemory错误

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

我正在使用应用程序what,它应该从数据库中获取用户列表,并从目录(ldap或AD)中更新这些详细信息。我在多核机器上做了什么,所以我创建了这个应用程序(代码如下)。我正在使用CompletionService并在Future对象中获得结果

过了一段时间,我收到了“无法创建新的本机线程”消息的内存不足错误。在TaskManager中,我看到应用程序创建了大量线程,但我要求创建大小等于处理器数量的固定线程池

我的代码有什么问题

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的假设是正确的。谢谢你的建议!