Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » Memory leak issue(Long living EntityManager causes memory leak)
icon5.gif  Memory leak issue [message #929435] Mon, 01 October 2012 13:05 Go to next message
Jérôme SALLES is currently offline Jérôme SALLESFriend
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 !


Re: Memory leak issue [message #929479 is a reply to message #929435] Mon, 01 October 2012 13:46 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1039
Registered: July 2009
Senior Member
Sounds like your EntityManagers are long lived in your sessionBeans. The repeatableWriteUnitOfWork is the cache used within an EntityManager to track managed entities; turning the shared cache off doesn't change that a local cache is used to keep track of entity changes. Detaching them just means the detached instances are not managed - depending on how this is done the EntityManager's cache can still reference epoxies and will grow for its life time. You can call me.clear() to cause the em to release all references, giving the opportunity for the memory to be gc'd eventually. Any entityManager will store the managed entity until it is cleared or evicted. If your query isn't modifying the entities, you might try using the read-only EclipseLink query hint.

Best regards,
Chris
icon9.gif  Re: Memory leak issue [message #929546 is a reply to message #929479] Mon, 01 October 2012 14:55 Go to previous messageGo to next message
Jérôme SALLES is currently offline Jérôme SALLESFriend
Messages: 10
Registered: October 2012
Junior Member
Thank you for your response.

In fact i use third EJB (3) to edit X's properties. So i cannot use the read only query hint.

In short, this is how the app work :


  1. Iteration : (1)'s EntityManger execute a query and paginate results for (2);
  2. Check : (2) checks X's datas and detach entities with (2)'s EntityManager (this is not a problem ?);
  3. Work : some work is applied with X's datas;
  4. Save : X instances are modified in (3). For that, i use an EntityManagerFactory and BeanManagedTransaction to apply transaction demarcation.


(2) and (3) run in parallel, so i cannot call clear in (2) without perturb (3). Also (3) is called concurrently (that's why i use an EntityManagerFactory).

So, if detach doesn't remove X from cache, how can i remove it without clear ?


Re: Memory leak issue [message #929586 is a reply to message #929546] Mon, 01 October 2012 15:33 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1039
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 16:24 Go to previous messageGo to next message
Jérôme SALLES is currently offline Jérôme SALLESFriend
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 !


Re: Memory leak issue [message #929761 is a reply to message #929645] Mon, 01 October 2012 18:46 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1039
Registered: July 2009
Senior Member
In 3 you show how you botain the entitymanager, but in 1+2 you just use the manager, so I assume you obtain one and just keep reusing it. You also implied that #1 and #2 are separate beans/processes. Unless they are using the same EntityManager instance, and that the isValidPeople() method isnt reading/merging entities, there is no reason for the detach process in #2. You can verify if the manager contains your entity using the em.contains() method, but the entities should not exist in that EntityManager unless you reuse the EM from #1. Even if you do, a better aproach might be to just clear the entitymanger after your query in step 1, as this detaches all entities read in through that query and EntityManager.

There is no point to detaching the entities in step 3 because you are closing the entityManager. All entities are automatically detached when the EM is closed or cleared - since the EntityManager no longer manages them. You can also do without the em.flush() as this is done when you call manager.getTransaction().commit();

Best Regards,
Chris
Re: Memory leak issue [message #930299 is a reply to message #929761] Tue, 02 October 2012 07:27 Go to previous messageGo to next message
Jérôme SALLES is currently offline Jérôme SALLESFriend
Messages: 10
Registered: October 2012
Junior Member
Thank you Chris, i will try your suggestions and come back in this post later to tell you the results. This will take 2 or 3 days...

Re: Memory leak issue [message #931573 is a reply to message #930299] Wed, 03 October 2012 10:21 Go to previous messageGo to next message
Jérôme SALLES is currently offline Jérôme SALLESFriend
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 !


Re: Memory leak issue [message #931869 is a reply to message #931573] Wed, 03 October 2012 15:49 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1039
Registered: July 2009
Senior Member
More information is needed on how the StartEvent instance is loaded to determine why it references a different EntityManager/UOW then the one you say was used to load it. It's unfetched lazy relationships should only reference the uow used to load it, so there is a problem if it references a different context. You haven't mentioned the EclipseLink version you are using, but you might want to try the latest release or nightly if you are not already.

Best Regards,
Chris
Re: Memory leak issue [message #931925 is a reply to message #931869] Wed, 03 October 2012 16:57 Go to previous messageGo to next message
Jérôme SALLES is currently offline Jérôme SALLESFriend
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 ?


Re: Memory leak issue [message #933010 is a reply to message #931925] Thu, 04 October 2012 16:47 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1039
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 07:05 Go to previous messageGo to next message
Jérôme SALLES is currently offline Jérôme SALLESFriend
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 ?


Re: Memory leak issue [message #934039 is a reply to message #933611] Fri, 05 October 2012 15:42 Go to previous messageGo to next message
Chris Delahunt is currently offline Chris DelahuntFriend
Messages: 1039
Registered: July 2009
Senior Member
RepeatableWriteUnitOfWork should not be shared among EntityManagers. The image you showed had the UOW referenced by the StartEntity through its changeTracking reference, so it must have originally been loaded in that UOW, and that UOW used to load a People entity as well. People can get loaded directly though queries, or indirectly through relationships. If lazy relationships are used, it would be easy enough for the People entity to get into the cache after the clear call. I'd recommend having JPA use field access and have the appliation use getters/setters so that you can add debugging to all points that a People entity might be accessed from. You might also turn on logging to see if/when it gets queried from the database

You can also check what is in the cache at any time by using native EclipseLink getIdentityMapAccessor().printIdentityMaps() call on the UnitOfWork within the entityManager, which you can obtain from ((JpaEntityManager)em.getDelegate()).getUnitOfWork().

You might want to start with a smaller version of your app and verify what is going on and then work up to the app having the problem - as you mention there are various processes doing many things where anyone of them could be causing StartEvent and People to be loaded in the same EM context - for instance how is the StartEvent sent to the MDB?

Best Regards,
Chris
Re: Memory leak issue [message #937706 is a reply to message #934039] Tue, 09 October 2012 07:39 Go to previous messageGo to next message
Jérôme SALLES is currently offline Jérôme SALLESFriend
Messages: 10
Registered: October 2012
Junior Member
Thnaks Chris for your response.

I am on this project today and i will try to see what is loaded and when with the snippet you give me.

I will be back tomorrow or thursday Nod


Re: Memory leak issue [message #937832 is a reply to message #937706] Tue, 09 October 2012 09:57 Go to previous messageGo to next message
Jérôme SALLES is currently offline Jérôme SALLESFriend
Messages: 10
Registered: October 2012
Junior Member
I have read this thread entirely one more time, and i am now sure that Chris is right. But you already know that Smile

The People cached in the first part of this post (before changing managed.detach in (2) to manager.clear in (1)) are not in memory for the same reason in the second part. This detail was making me confuse.

In first part, i never detach People from (1). In second part the problem is that People are in cache because of traversing relationship from StartEvent !

Even if StartEvent is detached, as you can see, People that are fetched indirectly by StartEvent are stored in the cache of the EntityManager that have load StartEvent. This is exactly what Chris says :

Quote:

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


People are indirectly loaded because i compute the size of a lazy ManyToMany collection of them in an entity indirectly fetched through StartEvent (i hope i am clear).

My NEW question is :

Are People lazyly fetched by this relation stored in the cache of the EntityManager which was used for loading the StartEvent ? If this is the case, how can i avoid this behavior ?

Thanks for all !


Re: Memory leak issue [message #941062 is a reply to message #937832] Fri, 12 October 2012 07:25 Go to previous message
Jérôme SALLES is currently offline Jérôme SALLESFriend
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.


Previous Topic:Moxy JPA+JAXB mapping issue with list unwrapping
Next Topic:Annotation @Lob - Trying to save files on Oracle DB
Goto Forum:
  


Current Time: Fri Nov 28 20:52:11 GMT 2014

Powered by FUDForum. Page generated in 0.02221 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software