我曾尝试优化(内存)程序,但GC仍使其滞后

时间:2018-08-29 08:31:33

标签: java garbage-collection profiling

我用Java编写了一个软件,该软件通过使用代理发送HTTP请求来检查代理是否正常工作。

它从数据库中获取大约30,000个代理,然后尝试检查它们是否可运行。从数据库接收到的代理曾经以ArrayList<String>的形式返回,但由于以下原因已更改为Deque<String>

程序的工作方式是有一个ProxyRequest对象,该对象分别将IP和端口存储为String和int。 ProxyRequest对象具有方法isWorkingProxy(),该方法尝试使用代理发送请求并返回boolean是否成功。

ProxyRequest对象由RunnableProxyRequest对象包裹,该对象在重写的super.isWorkingProxy()方法中调用run()。根据{{​​1}}的响应,super.isWorkingProxy()对象将更新MySQL数据库。

请注意,MySQL数据库的更新为RunnableProxyRequest

  

它使用FixedThreadPool(在VPS上)在750个线程上运行,但朝   最后,它变得非常慢(卡在〜50个线程上),这显然   表示垃圾收集器正在工作。这是问题。

我已经尝试了以下方法来改善延迟,但似乎不起作用:

1)使用synchronized()代理并使用Deque<String>获得代理所在的Deque.pop()。 (我相信)可以使String不断变小,从而可以改善由GC引起的延迟。

2)设置Deque<String>,其中con.setConnectTimeout(this.timeout);这样,连接应在5秒钟内返回结果。如果没有,则该线程已完成,并且不应再在线程池中处于活动状态。

除此之外,我不知道有什么其他方法可以提高性能。

有人可以为我推荐一种方法来提高性能,以防止/停止GC滞后于线程末尾吗?我知道有一个关于{Java threads slow down towards the end of processing)的Stackoverflow问题,但是我已经尝试了答案中的所有内容,但它对我没有用。

谢谢您的时间。

代码段:

循环向this.timeout = 5000;添加线程:

FixedThreadPool

//This code is executed recursively (at the end, main(args) is called again) //Create the threadpool for requests //Threads is an argument that is set to 750. ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newFixedThreadPool(threads); Deque<String> proxies = DB.getProxiesToCheck(); while(proxies.isEmpty() == false) { try { String[] split = proxies.pop().split(":"); Runnable[] checks = new Runnable[] { //HTTP check new RunnableProxyRequest(split[0], split[1], Proxy.Type.HTTP, false), //SSL check new RunnableProxyRequest(split[0], split[1], Proxy.Type.HTTP, true), //SOCKS check new RunnableProxyRequest(split[0], split[1], Proxy.Type.SOCKS, false) //Add more checks to this list as time goes... }; for(Runnable check : checks) { executor.submit(check); } } catch(IndexOutOfBoundsException e) { continue; } } 类:

ProxyRequest

//Proxy details private String proxyIp; private int proxyPort; private Proxy.Type testingType; //Request details private boolean useSsl; public ProxyRequest(String proxyIp, String proxyPort, Proxy.Type testingType, boolean useSsl) { this.proxyIp = proxyIp; try { this.proxyPort = Integer.parseInt(proxyPort); } catch(NumberFormatException e) { this.proxyPort = -1; } this.testingType = testingType; this.useSsl = useSsl; } public boolean isWorkingProxy() { //Case of an invalid proxy if(proxyPort == -1) { return false; } HttpURLConnection con = null; //Perform checks on URL //IF any exception occurs here, the proxy is obviously bad. try { URL url = new URL(this.getTestingUrl()); //Create proxy Proxy p = new Proxy(this.testingType, new InetSocketAddress(this.proxyIp, this.proxyPort)); //No redirect HttpURLConnection.setFollowRedirects(false); //Open connection with proxy con = (HttpURLConnection)url.openConnection(p); //Set the request method con.setRequestMethod("GET"); //Set max timeout for a request. con.setConnectTimeout(this.timeout); } catch(MalformedURLException e) { System.out.println("The testing URL is bad. Please fix this."); return false; } catch(Exception e) { return false; } try( BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); ) { String inputLine = null; StringBuilder response = new StringBuilder(); while((inputLine = in.readLine()) != null) { response.append(inputLine); } //A valid proxy! return con.getResponseCode() > 0; } catch(Exception e) { return false; } } 类:

RunnableProxyRequest

public class RunnableProxyRequest extends ProxyRequest implements Runnable { public RunnableProxyRequest(String proxyIp, String proxyPort, Proxy.Type testingType, boolean useSsl) { super(proxyIp, proxyPort, testingType, useSsl); } @Override public void run() { String test = super.getTest(); if(super.isWorkingProxy()) { System.out.println("-- Working proxy: " + super.getProxy() + " | Test: " + test); this.updateDB(true, test); } else { System.out.println("-- Not working: " + super.getProxy() + " | Test: " + test); this.updateDB(false, test); } } private void updateDB(boolean success, String testingType) { switch(testingType) { case "SSL": DB.updateSsl(super.getProxyIp(), super.getProxyPort(), success); break; case "HTTP": DB.updateHttp(super.getProxyIp(), super.getProxyPort(), success); break; case "SOCKS": DB.updateSocks(super.getProxyIp(), super.getProxyPort(), success); break; default: break; } } } 类:

DB

1 个答案:

答案 0 :(得分:1)

感谢Peter Lawrey指导我解决问题! :)
他的评论:

  

@ILoveKali我发现网络库在   发生错误时关闭连接。超时倾向   当连接良好时,可以发挥最佳效果。 YMMV

因此,我做了一些研究,发现我还必须使用方法setReadTimeout(this.timeout);。以前,我只使用setConnectTimeout(this.timeout);

感谢这篇帖子(HttpURLConnection timeout defaults)解释了以下内容:

  

不幸的是,根据我的经验,使用这些默认设置可以   导致不稳定状态,具体取决于您发生的情况   与服务器的连接。如果您使用HttpURLConnection而不   明确设置(至少读取)超时,您的连接可以进入   永久的陈旧状态。默认情况下。因此,始终将setReadTimeout设置为   “某物”,或者您可能孤立连接(甚至可能是线程)   取决于您的应用运行方式。

因此,最后的答案是:GC的运行很好,它对延迟不负责。线程只是永久地固定在一个数字上,因为我没有设置读取超时,因此isWorkingProxy()方法永远不会得到结果并保持读取状态。