Home » Eclipse Projects » EclipseLink » Memory leak issue(Long living EntityManager causes memory leak)
Memory leak issue [message #929435] |
Mon, 01 October 2012 09:05  |
Jérôme SALLES Messages: 10 Registered: October 2012 |
Junior Member |
|
|
Hi all,
I have a memory leak issue in my application. This application iterates overs 350000 objects X to do its works. This iteration can take 1 one or days.
My PersistenceUnit is in JTA mode in persistence.xml. I also put the shared cache mode at NONE (because each of my object will be read only once).
After one week of processing, the virtual machine memory can grow up to 2Gb and causes its crash (and also the glassfish domain that run on it).
I have a heap dump of the memory. I cannot post it here because of its size, but i can query it if you want). It shows that the memory is full of instance of my object X. I am not an expert in JPA, but it seems that those instance are keep in memory because of 67 instances of RepeatableWriteUnitOfWork.
First question : are the instance of RepeatableWriteUnitOfWork the cache of EclipseLink ? If yes, why is there a cache despit of the shared cche mode set to none ?
When i read X objects, i use a session bean (1) which executes the JPQL query with its EntityManager. Then i use them in another session bean (2) which detach X entities from its own EntityManager.
Second question : is detaching mandatory to evict X instance from Entity Manager's cache ? I detach the entity using (2)'s EntityManager : should i do it from (1)'s EntityManager ?
Thank for your responses !
Mon CV d'architecte logiciel Java EE
|
|
| | |
| Re: Memory leak issue [message #929586 is a reply to message #929546] |
Mon, 01 October 2012 11:33   |
Chris Delahunt Messages: 862 Registered: July 2009 |
Senior Member |
|
|
Hello,
How are you registering Entities from #1 in #2 and #3? Are you re-reading them or merging them somehow, and if so, why? How are you detaching them? If you are not using the entities in bean 1 or 2, why do you need them to be managed instances - a read-only hint will makes them detached.
I'm not really sure why you can't call clear. Without clearing the entitymanger between query calls, all entities are being stored in the EntityManager cache. So you are not gaining anything by using pagination since all entities are being brought into the cache by the end anyway. You should be able to use clear at logic points - clear just releases EntityManager's references to managed entities, detaching all entities and allowing GC to clean up the references. It works similar to em.detach except on a larger scale - if using detach on select entities, you could be leaving entities that reference your detached entity in the cache. Since your entityManager is long lived, these entities and their references remain in the cache until it is closed or cleared. Effectively you may be detaching them from EM #2, but leaving entities in the cache that still reference your detached entities, and there are still managed entities representing this data EMs #1+3.
You also mentioned you have 67 RepeatableWriteUnitOfWork. This represents 67 EntityManagers, so either it is under load, or you are keeping alot of EntityManagers and their caches around for a long period without releasing them to be garbage collected.
Best Regards,
Chris
|
|
|
| Re: Memory leak issue [message #929645 is a reply to message #929586] |
Mon, 01 October 2012 12:24   |
Jérôme SALLES Messages: 10 Registered: October 2012 |
Junior Member |
|
|
Thank you for your response.
In my heap dump i see 65 RepeatableWriteUnitOfWork and 48 EntityManagerWrapper.
In (1), i load X (you will see People) like this :
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
private void nextPage() {
logger.log(Level.INFO, "L''itérateur de people va changer de page. La prochaine page commencera au People [{0}] et comportera [{1}] objet(s) au maximum", new Object[]{
minBound, getPageSize()
});
try {
this.dynamicList = manager.createQuery(query, People.class).setFirstResult(minBound).setMaxResults(getPageSize()).getResultList();
logger.log(Level.INFO, "La page comporte [{0}] \u00e9l\u00e9ments.", dynamicList.size());
this.dynamicIterator = dynamicList.listIterator();
minBound += dynamicList.size();
} catch (Exception e) {
logger.log(Level.INFO, "Impossible de changer de page : {0}", e.getMessage());
}
}
Even if i let the entity in cache, i see an interest to load my People page per page : the resulting list after invocation of getResultList is known. But i agree with you : it would be better to not store them in the cache. How can i do that ?
In (2), i do something like this :
@Override
public boolean hasNext() {
boolean result = false;
while (!(result = isValidPeople()) && targetEnumeratorEjb.hasNext()) {
if (currentPeople != null) {
manager.detach(currentPeople);
}
currentPeople = targetEnumeratorEjb.next();
}
if (currentWayCom != null) {
manager.detach(currentWayCom);
manager.detach(currentWayCom.getPeople());
}
return result;
}
I recognize that this kind of iteration is a bit strange but this part of the code is complex... As you can see, i detach my people as soon as the test isValidPeople is done. This test doesn't write in people : the test could be done on a detached entity.
In (3) i edit datas like this (there is another method that work in the same way) :
@Override
public void invalidate(Mailing mailing, Recipient recipient) {
WayComAdapter adapter = (WayComAdapter) recipient;
if (adapter != null && adapter.getWayCom() != null) {
EntityManager manager = getManager();
WayCom wayCom = manager.merge(adapter.getWayCom());
wayCom.setStatus(WayComStatus.INVALID);
wayCom.setEnabled(false);
adapter.setWayCom(wayCom);
manager.flush();
manager.detach(wayCom.getPeople());
manager.detach(wayCom);
manager.getTransaction().commit();
manager.close();
}
}
private EntityManager getManager() {
try {
EntityManager result = factory.createEntityManager();
result.getTransaction().begin();
return result;
} catch (Exception ex) {
Logger.getLogger(RoutingHandlerBean.class.getName()).log(Level.SEVERE, "Cannot start transaction : {0}", ex.getMessage());
return null;
}
}
I merge the entity with the current entitymanager build in the method getManager.
I think that you are right : i should use clear on (1) and (2). For (3), it's not necessary as i call close on my EntityManager. That's right ?
Thank you !
Mon CV d'architecte logiciel Java EE
|
|
| | |
| Re: Memory leak issue [message #931573 is a reply to message #930299] |
Wed, 03 October 2012 06:21   |
Jérôme SALLES Messages: 10 Registered: October 2012 |
Junior Member |
|
|
Hi all,
I have made Chris's modification, and it works well now for the three bean that i was speaking last time ! Thank you chris !
This problem is solved, but it shows another problem. I see now another reference on People object in an EntityManager which doesn't load them ! Those instances of People are still in its cache, and i don't understand why...
If i take one of this People and i apply the function "Show Nearest GC" (which should tell me why the object is in memory) in jvisualvm, i see that this object is linked by a RepeatableWriteUnitOfWork (uow). Uow is itself linked to an entity of another type (StartEvent). This kind of object (StartEvent) is loaded by an EntityManager. This EntityManager is not the same that EntityManager which loads People.
I don't know if i am clear... If this explanation is not clear, please send question !
So the question is : how a RepeatableWriteUnitOfWork can reference two types of Entity which aren't load by the same EntityManager ?
Thanks !
Mon CV d'architecte logiciel Java EE
|
|
| |
| Re: Memory leak issue [message #931925 is a reply to message #931869] |
Wed, 03 October 2012 12:57   |
Jérôme SALLES Messages: 10 Registered: October 2012 |
Junior Member |
|
|
Chris,
Thank you for your response. The version i use is 2.3.0 on a glassfish server 3.1.1.
StartEvent is an entity which is the object of an MDB message. I reload it only if the EntityManager injected inside the message consumer doesn't contain it. if so, i refresh the entity from db, do the work, then detach the entity :
Event event = (Event) content;
if (event == null) {
return null;
} else {
Event result = null;
if (event.getId() != null) {
result = manager.find(Event.class, event.getId());
}
if (result == null) {
manager.persist(event);
manager.flush();
result = event;
}
if (manager.contains(event)) {
manager.detach(event);
}
if (result.isEnabled()) {
return result;
} else {
return null;
}
}
That's the only thing i do with this EntityManager.
What makes me say that the EntityManager which loads StartEvent has references on People is the screenshot joined to this message.
What is your opinion ?
Mon CV d'architecte logiciel Java EE
|
|
|
| Re: Memory leak issue [message #933010 is a reply to message #931925] |
Thu, 04 October 2012 12:47   |
Chris Delahunt Messages: 862 Registered: July 2009 |
Senior Member |
|
|
The screenshot and the code lead me to believe this is the same as the intial issue. Your EntityManagers are long lived, and you are only removing the single entity from the cache that was read in - but you haven't shown any of the relationships in your entities. When building an object, these too can get brought in depending on the eager/fetch settings. Since you are only ever removing the owning entity and never clearing/closing the EM- the related entities will remain in the cache for the life of the app.
There is no way to tell from what you've provided for sure how the People entity got in the cache, but I suspect StartEntity or some other object has references to People. If they are Eager, then the referenced People entities will be immediately be managed and placed in the cache. If they are lazy, they will be put in the cache if and when the relationship is accessed. One thing to note about EclipseLink behavior is that EclipseLink allows traversing lazy relationships on detached entities as long as the context is still available. This might be a problem for your cache since you never close the EntityManager, so it is always available. You also mentioned you are not explicietely loading People from this EM so I guess you never explicietely remove it from the cache either.
You will need to look over your app design and either close and reobtain EntityManagers at points, clear them, or a combination of both. Reusing the same EntityManager without clearing allows it to build up references.
Please also note that in the code above, manager.contains(event) should always return true, so you might want to just call clear at this point without the check. I'd recommend refactoring your methods to keep the EntityManager local to the method, and initially call a getEntityManager() method that can create a new one or provide the existing one if in a transaction.
Best Regards,
Chris
|
|
|
| Re: Memory leak issue [message #933611 is a reply to message #933010] |
Fri, 05 October 2012 03:05   |
Jérôme SALLES Messages: 10 Registered: October 2012 |
Junior Member |
|
|
Chris, Thank you for your reply.
I have effectively one people in my StartEvent. This people is not read from it and so i think this people is never cached, but i could be wrong...
Furthermore, People i see in the screenshot have no relation with the startevent. Those People are load from a query by (3), page per page, and the entitymanager is cleared after each page. The code look like this after your suggestion :
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
private void nextPage() {
logger.log(Level.INFO, "L''itérateur de people va changer de page. La prochaine page commencera au People [{0}] et comportera [{1}] objet(s) au maximum", new Object[]{
minBound, getPageSize()
});
try {
this.dynamicList = manager.createQuery(query, People.class).setFirstResult(minBound).setMaxResults(getPageSize()).getResultList();
logger.log(Level.INFO, "La page comporte [{0}] \u00e9l\u00e9ments.", dynamicList.size());
this.dynamicIterator = dynamicList.listIterator();
//http://www.eclipse.org/forums/index.php/mv/msg/389907/929761/#msg_929761
manager.clear();
minBound += dynamicList.size();
} catch (Exception e) {
logger.log(Level.INFO, "Impossible de changer de page : {0}", e.getMessage());
}
}
For me, RepeatableWriteUnitOfWork can be shared between different EntityManager (that's why StartEvent and People are in the same one). The only problem is : why manager.clear() is not applied in the code above ?
Mon CV d'architecte logiciel Java EE
|
|
| | | |
| Re: Memory leak issue [message #941062 is a reply to message #937832] |
Fri, 12 October 2012 03:25  |
Jérôme SALLES Messages: 10 Registered: October 2012 |
Junior Member |
|
|
I have read somewhere that objets loaded from lazy relationships on detached entities are detached too.
After have a look to the screenshot, we can see that the Garbage Collection Root of the People is an Executor which is observed by an object which keep itself a reference on the StartEvent.
The only thing i have to do is not to read the relationship which loads People, and i think i can do it.
Mon CV d'architecte logiciel Java EE
|
|
|
Goto Forum:
Current Time: Sun May 19 16:21:00 EDT 2013
Powered by FUDForum. Page generated in 0.02221 seconds
|