简体   繁体   English

运行Apache DefaultHttpClient并在Tomcat中重新部署Web应用意味着Permgen空间

[英]Running Apache DefaultHttpClient and redeploying web app in Tomcat means Permgen space

I have been around this problem few months and now I am sure HttpClient is the responsable of my permament PermGen Space errors. 我几个月来一直在解决这个问题,现在我可以确定我的PermGen Space错误是由HttpClient负责的。

My web app uses a Servlet. 我的Web应用程序使用Servlet。 One of the functionalities allows users to connect to remote devices and request via HTTP status of this devices using REST services. 功能之一允许用户连接到远程设备,并使用REST服务通过该设备的HTTP状态进行请求。

The problem arises when I redeploy Tomcat (in development time, I am not in production) and PermGen appears. 当我重新部署Tomcat(在开发时,我不在生产中)并且出现PermGen时,就会出现问题。 I know the problem is related to this HttpClient because of the trace: 由于跟踪,我知道问题与此HttpClient有关:

jun 19, 2013 4:51:34 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: El Servlet.service() para el servlet [dispatcher] en el contexto con ruta [] lanzó la excepción [Handler processing failed; nested exception is java.lang.OutOfMemoryError: PermGen space] con causa raíz
java.lang.OutOfMemoryError: PermGen space
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2904)
    at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1173)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1681)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
    at org.apache.http.impl.cookie.RFC2109Spec.<init>(RFC2109Spec.java:83)
    at org.apache.http.impl.cookie.RFC2965Spec.<init>(RFC2965Spec.java:67)
    at org.apache.http.impl.cookie.BestMatchSpec.getStrict(BestMatchSpec.java:75)
    at org.apache.http.impl.cookie.BestMatchSpec.getVersion(BestMatchSpec.java:209)
    at org.apache.http.client.protocol.RequestAddCookies.process(RequestAddCookies.java:202)
    at org.apache.http.protocol.ImmutableHttpProcessor.process(ImmutableHttpProcessor.java:109)
    at org.apache.http.protocol.HttpRequestExecutor.preProcess(HttpRequestExecutor.java:176)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:519)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:784)
    at com.pe.f.web.PlantController.getStatus(PlantController.java:431)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:100)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:604)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:565)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)

Where 哪里

at com.pe.f.web.PlantController.getStatus(PlantController.java:431)

Is the method that launches the HTTP request. 是启动HTTP请求的方法。

I have two main questions I need to solve: 我有两个主要问题需要解决:

1- Is this problem specificly related to HttpClient or am I doing something wrong? 1-这个问题具体与HttpClient有关还是我做错了什么?

Here is my code: 这是我的代码:

for(Gdu g : p.getGduCollection()){

    try {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 30000);
        get = new HttpGet("http://" 
                        + p.getInstallationId().getIprouter() 
                        + ":" 
                        + g.getHttpPort().toString()
                        + "/FSP/status");
        HttpResponse response = httpClient.execute(get);

        if (response.getStatusLine().getStatusCode() != 200) {
            throw new RuntimeException("Failed : HTTP error code : "
                           + response.getStatusLine().getStatusCode());
        }
        StringBuilder inputStringBuilder = new StringBuilder();
        BufferedReader br = new BufferedReader(
                         new InputStreamReader((response.getEntity().getContent())));
        String line;

        while ((line = br.readLine()) != null) {
            if(!line.contains("<?xml")){
                inputStringBuilder.append(line);
            }                    
        }

        httpClient.getConnectionManager().shutdown();
        logger.debug(inputStringBuilder.toString());
        Document xml = loadXMLFromString(inputStringBuilder.toString());

        // sets the date
        status.setDate(new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").parse(xml.getElementsByTagName("time").item(0).getTextContent()));

        // add power of each GDU
        Float f = Float.parseFloat(status.getPower().split(" ")[0]);
        f += Float.parseFloat(xml.getElementsByTagName("power").item(0).getTextContent().split(" ")[0]);
        status.setPower(f.toString() + " kW");

        // add the energy of each GDU
        Float f2 = Float.parseFloat(status.getEnergy().split(" ")[0].replace(",", "."));
        f2 += Float.parseFloat(xml.getElementsByTagName("energy").item(0).getTextContent().split(" ")[0]);
        status.setEnergy(String.format("%.3f", f2 - 0.005).replace(",", ".") + " MWh");

        NodeList nl = xml.getElementsByTagName("status");
        stat = 0;
        for(int j=0; j < nl.getLength(); j++){
            if(nl.item(j).getNodeType() == Node.ELEMENT_NODE){ // Node
                Element e = (Element) nl.item(j);
                stat += Integer.valueOf(e.getElementsByTagName("value").item(0).getTextContent());
            }
        }

        status.setPlantStatus(String.valueOf(stat + Integer.parseInt(status.getPlantStatus())));

        } catch (ConnectTimeoutException cte ) {
            status.setConnError("Unnable to communicate with plant. Reason: Connection Timeout.");
        } catch (SocketTimeoutException ste) {
            status.setConnError("Unnable to communicate with plant. Reason: Socket Timeout.");
        } catch (IOException ex) {
            status.setConnError("Unnable to communicate with plant. Reason: I/O Exception.");

        } finally {
            if(get != null){
                get.releaseConnection();
            }                
        }        
    }

My code opens a HTTP connection, requests the status and receives XML as a String. 我的代码打开一个HTTP连接,请求状态并接收XML作为字符串。 Once I get the XML I create an Status object with this information. 获得XML后,我将使用此信息创建一个Status对象。

2- Do you suggest any other thinner HTTP client? 2-您是否建议使用其他更瘦的HTTP客户端? HttpURLConnection maybe? HttpURLConnection也许吗?

I know most of you will suggest to increase the Permament memory heap. 我知道你们大多数人都会建议增加Permament内存堆。 I have read a lot on this topic. 我已经阅读了很多有关该主题的文章。 I don't see the point to increase the heap size beacuse in the end, if the problem should appear it will appear. 我最终看不到增加堆大小的意义,如果出现问题,它将出现。

Thanks in advance 提前致谢

Update: Following SO post I have avoided the automatic retry request in HttpClient. 更新:SO post之后,我避免了HttpClient中的自动重试请求。 I will keep performing tests in development server and production server before accept any answer. 在接受任何答案之前,我将继续在开发服务器和生产服务器中执行测试。

References: 参考文献:

I doubt that the problem is with HttpClient. 我怀疑问题出在HttpClient。 Application redeploys tend to to eat up perm gen space because classes are being reloaded (and placed into perm gen space) upon every redeploy. 应用程序重新部署往往会占用烫发代空间,因为每次重新部署时都会重新加载类(并将它们放入烫发代空间)。

If the problem only appears during development due to application redeploys, then increasing perm gen size is the way to go. 如果问题仅由于应用程序重新部署而出现在开发过程中,那么增加烫发的大小是解决之道。 You can also try out eg JRebel to avoid full redeploys. 您也可以尝试使用例如JRebel以避免完全重新部署。 Also restarting Tomcat every now and then helps. 偶尔重新启动Tomcat也会有所帮助。

您可以使用以下命令行参数允许开发tomcat卸载所有perm gen类:

 -XX:+CMSClassUnloadingEnabled

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM