Java 执行器服务内存不足错误

Java 执行器服务内存不足错误,java,multithreading,Java,Multithreading,我有一个ArrayList,它有数十亿条记录,我迭代每个记录并将其发布到服务器。在每次迭代中调用的方法如下所示: public void sendNotification(String url, String accountId, String accountPwd, String jsonPayLoad, int maxConnections) { notificationService = Executors.newFixedThreadPool(ma

我有一个ArrayList,它有数十亿条记录,我迭代每个记录并将其发布到服务器。在每次迭代中调用的方法如下所示:

public void sendNotification(String url, String accountId, String accountPwd, String jsonPayLoad,
            int maxConnections) {
        notificationService = Executors.newFixedThreadPool(maxConnections);
        notificationService.submit(new SendPushNotification(url, accountId, accountPwd, jsonPayLoad));
        notificationService.shutdown();

    }
我的SendPushNotification类如下所示:

public class SendPushNotification implements Runnable {

    String url;
    String accountId;
    String accountPwd;
    String jsonPayLoad;

    public SendPushNotification(String url, String accountId, String accountPwd, String jsonPayLoad) {
        this.url = url;
        this.accountId = accountId;
        this.accountPwd = accountPwd;
        this.jsonPayLoad = jsonPayLoad;

    }
    public void run() {

        HttpsURLConnection conn = null;
        try {

            StringBuffer response;
            URL url1 = new URL(url);
            conn = (HttpsURLConnection) url1.openConnection();
            // conn.setReadTimeout(20000);
            // conn.setConnectTimeout(30000);
            conn.setRequestProperty("X-Account-Id", accountId);
            conn.setRequestProperty("X-Passcode", accountPwd);
            conn.setRequestProperty("Content-Type", "application/json");
            conn.setDoOutput(true);
            conn.setRequestMethod("POST");

            OutputStream out = new BufferedOutputStream(conn.getOutputStream());
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
            writer.write(jsonPayLoad);
            writer.close();
            out.close();
            int responseCode = conn.getResponseCode();

            System.out.println(String.valueOf(responseCode));
            switch (responseCode) {
            case 200:
                BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                String inputLine;
                response = new StringBuffer();
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
                System.out.println(response.toString());
            }
        } catch (IOException ez) {
            ez.printStackTrace();

        } finally {
            if (conn != null) {
                try {
                    conn.disconnect();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    }

所以这里出了什么问题,我怀疑我必须在一个像样的系统配置上运行它。基本上我想了解我的代码是否有任何问题

错误的方法!循环:

notificationService = Executors.newFixedThreadPool(maxConnections);
这是个坏主意!为什么您打算创建数十亿个线程池;提交一个任务然后关闭它?!这就像每次烟灰缸满了都要买一辆新的法拉利

请理解:您的简单代码创建了相当多的对象;在一次循环迭代之后,所有这些都消失了。意思是:他们有资格进行垃圾收集。换句话说:你不断地以很高的速度制造垃圾。你真的很惊讶这样做会让你“失去记忆”

相反,使用oneThreadPool并将数十亿的请求提交到该线程池中

除此之外,这也不是一个好主意。每个条目打开一个到服务器的网络连接将不会扩展到数十亿个条目:真正的解决方案要求您后退一步,想出一种端到端的“合理方式”。例如,您应该考虑在服务器上创建某种“批量”或“流”接口。在客户机上迭代数十亿个条目文件,并与服务器建立数十亿个连接,这是很抱歉的,疯狂

因此,不要这样做:

loop:
  open connection / push ONE item / close connection
你最好选择:

open connection / push all items / close connections
或者除此之外,您甚至可以考虑传输压缩的二进制数据。意思是:在客户端压缩文件,将其作为blob发送;并在服务器端提取/处理它

这里有很多选择空间;但请放心:您当前的“内存不足”异常只是由“不适当”设计引起的一个症状

编辑:根据您的评论,我的(个人)建议:

  • 显然:使用onethreadpool(可能构建在3个线程上)并将您的Runnable推送到该共享池中
  • 然后开始仔细分析。考虑到您打算处理这数十亿条记录,每毫秒可能都很重要。换句话说:进行合理的测试,找出解决方案有多“好”。如果不成功;进行分析,以发现需要改进的地方
  • 要理解的关键点是:你可能可以对事情进行微调,在这里获得1%,在那里获得5%;但很可能所有这些都不够好。当你做了10亿次的事情,那么它们应该非常快;否则这个因素会杀了你
执行器服务内存不足错误

正如@GhostCat指出的,您应该在程序开始时在通知类的顶部创建一个
ExecutorService
。当您收到新请求时,您将所有通知请求提交给相同的
ExecutorService
,该服务将根据需要创建线程。每次通过创建一个新池都是一种糟糕的模式

// at top of class not in the sending loop 
private final ExecutorService notificationThreadPool =
     Executors.newFixedThreadPool(MAX_CONNECTIONS);
但是,重要的是要认识到,由于线程池使用了一个无界队列,所以内存可能仍然不足。封面下的
newFixedThreadPool(…)
方法使用
newlinkedblockingqueue()
,因此如果您的发件人没有跟上需求,您的内存仍然会不足

然后,如果增加线程数量不是一个选项,那么您将不得不以某种方式降低生产者的速度,或者将请求写入磁盘或其他短期存储

如果您需要使用一个有界队列来检测什么时候东西正在填满,那么您应该执行以下操作:

private final ExecutorService notificationThreadPool =
     new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
                            new LinkedBlockingQueue<Runnable>(MAX_QUEUED_REQUESTS);
专用最终执行器服务通知线程池=
新的ThreadPoolExecutor(第n个线程,第n个线程,第0个线程,时间单位为毫秒,
新LinkedBlockingQueue(最大排队请求数);

默认情况下,当您向池提交
SendPushNotification
时,如果所有线程都很忙,并且队列已满,那么如果队列已满,它将抛出
RejectedExecutionException
,让您有机会以某种方式处理该异常。您还可以使用
notificationThreadPool.setRejectedExecutionHandler(…)
设置一个
RejectedExecutionHandler
,将被拒绝的通知写入临时存储,直到线程赶上。

哦,我正在一次又一次地实例化Executor服务!将其转换为我的主方法!只是出于好奇…你最终是如何解决这个问题的?很久以前,我使用了池连接e在应用程序的整个生命周期中,只需进行一次握手。因为我换了公司,忘记了这一点:DHi我真的很喜欢这个想法,但服务器不在我的控制范围内。服务器一次只接受一个负载,但我可以从一个ip发出三个并发请求。不客气。我做了一些更新。我ss您应该首先进行测量,以评估此架构是否能够在理论上运行。如果您发现“此架构永远不会扩展”,那么除了更改整个架构之外别无选择。当服务器无法支持需求时(向其推送数十亿条条目),使用“此服务器”有什么意义.换句话说:你想解决你的问题。如果“那个服务器”阻止了解决方案,那么它就必须停止!公司兄弟公司!我希望事情能在我的控制下进行。不过你的建议帮了大忙,谢谢你,伙计,干杯:)