我们正在使用
Jprofiler
来分析我们的应用程序来解决内存泄漏,并发现以下观察结果:
我们在同一数据集上运行应用程序进行检查,并在标记堆后拍摄多个快照,以查看导致内存泄漏的类。
检查分配后的
byte[]
、String
、int[]
都指向JPA
查询。例如byte[]
的分配热点都指向查询结果:
QuerImpl.getSingleResult
,
QueryImpl.getResultList
EnitiyManger.find
:
对于
String
、jav.util.HashMap$Node
的分配也发现了类似的结果:
JPA 上显示重要分配的一个查询是
checkResourceID
类中的 Dao
:
@Stateless
@EJB(name = "java:global/ResourceProceduresDaoImpl", beanInterface = ResourceProceduresDao.class)
public class ResourceProceduresDaoImpl<R> implements ResourceProceduresDao<R> {
@Resource(mappedName = "java:/M2M_RESOURCES")
private DataSource ds;
@PersistenceContext(unitName = "EM")
EntityManager em;
static Logger logger = LogManager.getLogger(ResourceProceduresDaoImpl.class);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
public void create(R resource) {
em.persist(resource);
em.flush();
}
public R retrieve(Class type, String resourceID) {
logger.info("ResourceProceduresDaoImpl class=" + type + " resourceID=" + resourceID);
Object obj = em.find(type, resourceID);
// logger.info("ResourceProceduresDaoImpl obj="+obj);
return (R) obj;
}
public void update(R resource) {
em.merge(resource);
}
public void delete(R resource) {
if (!em.contains(resource))
resource = em.merge(resource);
em.remove(resource);
}
public boolean checkResourceID(String resourceID) {
boolean ifExists = false;
if (em.createNamedQuery("ResourcePCM.findByResourceID", ResourceParentChildMapping.class)
.setParameter("resourceId", resourceID).getSingleResult() == null) {
ifExists = true;
}
..//
return ifExists;
}
JPA
ResourcePCM
:
@Entity
@Table(name="\"RESOURCE_PCM\"",schema="\"RESOURCES\"")
@NamedQueries({
@NamedQuery(name="ResourcePCM.findAll", query="SELECT r FROM ResourcePCM r"),
@NamedQuery(name="ResourcePCM.findByResourceID", query="SELECT r FROM ResourcePCM r where r.resourceID = :resourceId")
})
@NamedStoredProcedureQuery(
name = "generateResourceID",
procedureName = "generateResourceID",
parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, type = String.class, name = "input"),
@StoredProcedureParameter(mode = ParameterMode.OUT, type = String.class, name = "output")
}
)
public class ResourcePCM implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name="\"resourceID\"")
private String resourceID;
@Column(name="\"structuredResourceID\"")
private String structuredResourceID;
@Column(name="\"parentID\"")
private String parentID;
@Column(name="\"resourceType\"")
private Integer resourceType;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
@JoinColumn(name="\"resourceID\"",referencedColumnName="\"resourceID\"")
private Set<ResourceACP> resourceACP;
..///
}
因此,只有在查询中才会有大量内存分配,而我无法找出原因。
是
JPA
吗?或者 EntityManager
的调用方式?
PS:我使用
JNDI
查找我的 Bean,而不是使用 @EJB
或 @Inject
进行注入,如果这会产生影响?
编辑
持久性.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="EM" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>java:/M2M_RESOURCES</jta-data-source>
<class>package.jpa.ResourceBase1</class>
<class>package.jpa.ResourceBase2</class>
<class>package.jpa.ResourceBase3</class>
<class>package.jpa.ResourceBase4</class>
<class>package.jpa.ResourceBase5</class>
<class>package.jpa.ResourceBase6</class>
..///
<!-- Converters -->
<class>package.mapping.DBJsonConverter</class>
<properties>
<property name="eclipselink.target-server" value="JBoss"/>
<property name="eclipselink.target-database" value="PostgreSQL" />
</properties>
</persistence-unit>
</persistence>
编辑2:
传入参考
byte[] :
传入参考文献已扩展:
5k请求和10k请求负载下拍摄的两张快照的区别:
postgresql-ds.xml
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<datasource jndi-name="java:/RESOURCES" pool-name="RESOURCES" enabled="true" use-java-context="true" use-ccm="true">
<connection-url>jdbc:postgresql://[hostname]:[port]/[schema]</connection-url>
<driver>postgresql-42.2.5.jar</driver>
<pool>
<min-pool-size>200</min-pool-size> <max-pool-size>400</max-pool-size>
<prefill>true</prefill>
</pool>
<security>
<user-name>[userName]</user-name>
<password>[password]</password>
</security>
<timeout>
<blocking-timeout-millis>800</blocking-timeout-millis>
</timeout>
<statement>
<prepared-statement-cache-size>100</prepared-statement-cache-size>
<share-prepared-statements>true</share-prepared-statements>
</statement>
</datasource>
</datasources>
IMO,调查问题的最佳方法是执行大量请求并进行堆转储。 您使用内存分析器分析堆转储 您打开“直方图”视图 您添加“保留堆”列 您对“保留堆”列进行排序。
现在您可以更好地了解对象和内存重新分区。 您可以通过右键单击>列出对象>使用传入引用来找到不同对象的来源。
您可以添加屏幕截图以获得更多帮助。