【问题标题】:Facing OutOfMemory issue in Java在 Java 中面临 OutOfMemory 问题
【发布时间】:2016-05-09 14:30:28
【问题描述】:

由于高堆分配,我面临内存不足的问题。我通过 HP 诊断工具对其进行了验证,它指向我的代码中我在数组列表中添加元素的部分。我无法弄清楚我还能如何编写此代码以便尽早释放对象。下面是代码:

    private List<UpperDTO> populateRecords(List<BaseEntity> baseEntityList,List<DataEntity> dataEntityList) {

    List<UpperDTO> masterDTOList  = new ArrayList<UpperDTO>();
    if(baseEntityList !=null && baseEntityList.size()>0){
        BigDecimal conId = null;
        for(BaseEntity baseEntity :baseEntityList){
            conId = baseEntity.getConsignmentId();
            ArrayList<StatusData> statusDataList = new ArrayList<StatusData>();
            if(dataEntityList !=null && dataEntityList.size()>0){
                for(DataEntity data : dataEntityList){
                    if(conId.equals(data.getConsignmentId())){
                        //making null to supress from the response 
                        data.setConsignmentId(null);
                        statusDataList.add(TrackServiceHelper.convertStatusDataToDTO(data));
                    }
                }
            }
            masterDTOList.add(TrackServiceHelper.populateDTO(baseEntity, statusDataList));  
        }
    }
    return masterDTOList;
}

public static UpperDTO populateDTO(TrackBaseEntity baseEntity,
        List<StatusData> statusList) {

    UpperDTO upperDTO = new UpperDTO();
    //Setter methods called
    upperDTO.setStatusData(statusList);
    return upperDTO;

}

问题指向代码中的以下行:

    masterDTOList.add(TrackServiceHelper.populateDTO(baseEntity, statusDataList));

这是从 JMS 队列接收消息的 rest api,MDB 监听这些消息。我无法在我的本地或开发环境中模拟这一点,因为问题出现在请求数量很高的性能测试期间。我该如何解决这个问题?

这是来自 HP Diagnostics 的 Collection Leak 的堆栈跟踪:

Chart   Collection Class    Contained Type  Probe   Collection Growth Rate  Collection Size Leak Stack Trace    Maximum Size
0, 0, 255   java.util.ArrayList com.rex.ih2.dtos.UpperDTO   gtatsh645   3,848   122,312 java.util.ArrayList.add(ArrayList.java:413)
com.rex.ih2.utils.AppDAO.populateConsignment(AppDAO.java:168)
com.rex.ih2.utils.AppDAO.searchConsignment(AppDAO.java:93)
com.rex.ih2.service.AppService.fetchConDetail(AppService.java:131)
com.rex.ih2.service.AppService.getConDetail(AppService.java:69)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:76)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:607)
org.apache.webbeans.intercept.InterceptorHandler.invoke(InterceptorHandler.java:297)
org.apache.webbeans.intercept.NormalScopedBeanInterceptorHandler.invoke(NormalScopedBeanInterceptorHandler.java:98)
com.rex.ih2.service.TrackService_$$_javassist_0.getConsignmentDetail(TrackService_$$_javassist_0.java)
com.rex.ih2.beans.TrackBean.action(TrackBean.java:35)
com.tnt.integration.bean.AbstractServiceBean.invokeService(AbstractServiceBean.java:259)
com.tnt.integration.bean.AbstractServiceBean.onMessage(AbstractServiceBean.java:157)
com.rex.ih2.beans.TrackBean.onMessage(TrackBean.java)

【问题讨论】:

  • 这是一个比您提供的两种方法大得多的问题。恕我直言,这更像是一个设计问题而不是代码问题。我想我在这里要问的问题是,这些信息是从哪里来的,又要去哪里?您提到了 JMS 和 MDB;是否可以一次处理更小块的信息?
  • 这是一个获取 API,它从消息代理接收请求消息,该消息代理具有很少的参数,必须根据这些参数在数据库中进行搜索。该搜索填充第一种方法中提到的 2 个实体。从这些实体中,我们必须填充一个 DTO,该 DTO 还具有另一个 DTO 的列表作为成员变量。然后在响应对象中设置此 DTO。
  • 那么你可以应用分页什么的吗? baseEntityListdataEntityList 到底有多大?也许您只需要更大的堆大小。
  • 大部分搜索的获取记录数不超过5,所以没有放入Nopagination。baseEntityList和dataEntityList分别有15个和6个变量。在 HP Diagnostics 的 Collection Leaks 中,它显示的集合增长率为 3848,集合大小为 122312,最大大小为 143291。我不确定为什么该列表增长如此之快。即使在发送响应之后,对象是否仍然是引用。响应发送后,垃圾回收列表是否可用?

标签: java arraylist collections memory-leaks message-driven-bean


【解决方案1】:

我同意 dcsohi。这实际上是一个设计问题。您可能想查看以下方法:-

1) 添加到列表中的对象的大小。如果可以优化。 2) 以块的形式处理数据,而不是一次在列表中添加所有数据。 3) 优化 JVM 参数以增加 head 大小,以便它可以处理更多对象。

您可以尝试通过增加测试对象的数量并减少开发环境中的堆大小来模拟这一点,或者可能进行生产转储并以相同的卷运行。

【讨论】:

  • 1) 我不确定如何减小对象的大小,因为它只有基本变量。 2)每个请求获取的数据量非常低,当请求数不高时会出现问题。 3)只有在没有其他选项来调整代码本身时,我才能使用此选项。
  • 检查列表的大小。看看你在评论中提到的,它看起来并没有太多。尝试进行堆转储。
【解决方案2】:

好的,在我看来,您只关心 DataEntity 对象和 BaseEntity 对象的“寄售 ID”匹配。你真的应该在数据库查询中做这种事情。 “实体”对象的使用使您的 DB 交互看起来像是通过 JPA/Hibernate,在这种情况下,您可能希望创建一个 DB 视图,通过寄售 ID 连接两个表,并为您的输出提供必要的信息。接下来,创建一个与该视图匹配的自定义只读实体。然后,您可以对该视图的查询应用分页(如果仍然需要)并以较小的批次检索信息。

【讨论】:

  • 我没有直接对数据库进行任何查询,因为查询非常复杂。我正在调用一个存储过程,它返回了 2 个引用,每个引用都包含描述中提到的实体列表。这就是为什么我必须遍历列表并查看第二个列表是否具有相同的托运 ID。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-29
  • 2011-02-25
  • 2011-09-26
  • 2018-01-26
  • 2020-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多