From 1b4eea59b6258ad5adc489167290c5b126417994 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Thu, 27 Jun 2019 15:54:45 +0100 Subject: [PATCH] HHH-13467 Make average BatchFetchQueue consume less memory --- .../hibernate/engine/spi/BatchFetchQueue.java | 66 +++++++++++++------ 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java index 892eb6b7a6..5b4e287617 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/BatchFetchQueue.java @@ -42,7 +42,7 @@ public class BatchFetchQueue { * A map of {@link SubselectFetch subselect-fetch descriptors} keyed by the * {@link EntityKey) against which the descriptor is registered. */ - private final Map subselectsByEntityKey = new HashMap<>( 8 ); + private Map subselectsByEntityKey; /** * Used to hold information about the entities that are currently eligible for batch-fetching. Ultimately @@ -51,13 +51,13 @@ public class BatchFetchQueue { * A Map structure is used to segment the keys by entity type since loading can only be done for a particular entity * type at a time. */ - private final Map > batchLoadableEntityKeys = new HashMap<>( 8 ); + private Map > batchLoadableEntityKeys; /** * Used to hold information about the collections that are currently eligible for batch-fetching. Ultimately * used by {@link #getCollectionBatch} to build collection load batches. */ - private final Map> batchLoadableCollections = new HashMap<>( 8 ); + private Map> batchLoadableCollections; /** * Constructs a queue for the given context. @@ -74,9 +74,9 @@ public class BatchFetchQueue { * Called after flushing or clearing the session. */ public void clear() { - batchLoadableEntityKeys.clear(); - batchLoadableCollections.clear(); - subselectsByEntityKey.clear(); + batchLoadableEntityKeys = null; + batchLoadableCollections = null; + subselectsByEntityKey = null; } @@ -90,6 +90,9 @@ public class BatchFetchQueue { * this entity key. */ public SubselectFetch getSubselect(EntityKey key) { + if ( subselectsByEntityKey == null ) { + return null; + } return subselectsByEntityKey.get( key ); } @@ -100,6 +103,9 @@ public class BatchFetchQueue { * @param subquery The fetch descriptor. */ public void addSubselect(EntityKey key, SubselectFetch subquery) { + if ( subselectsByEntityKey == null ) { + subselectsByEntityKey = new HashMap<>( 12 ); + } subselectsByEntityKey.put( key, subquery ); } @@ -110,7 +116,9 @@ public class BatchFetchQueue { * need to load its collections) */ public void removeSubselect(EntityKey key) { - subselectsByEntityKey.remove( key ); + if ( subselectsByEntityKey != null ) { + subselectsByEntityKey.remove( key ); + } } // entity batch support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -127,6 +135,9 @@ public class BatchFetchQueue { */ public void addBatchLoadableEntityKey(EntityKey key) { if ( key.isBatchLoadable() ) { + if ( batchLoadableEntityKeys == null ) { + batchLoadableEntityKeys = new HashMap<>( 12 ); + } final LinkedHashSet keysForEntity = batchLoadableEntityKeys.computeIfAbsent( key.getEntityName(), k -> new LinkedHashSet<>( 8 ) @@ -143,9 +154,9 @@ public class BatchFetchQueue { * if necessary */ public void removeBatchLoadableEntityKey(EntityKey key) { - if ( key.isBatchLoadable() ) { - LinkedHashSet set = batchLoadableEntityKeys.get( key.getEntityName()); - if (set != null) { + if ( batchLoadableEntityKeys != null && key.isBatchLoadable() ) { + LinkedHashSet set = batchLoadableEntityKeys.get( key.getEntityName() ); + if ( set != null ) { set.remove(key); } } @@ -155,8 +166,8 @@ public class BatchFetchQueue { * Intended for test usage. Really has no use-case in Hibernate proper. */ public boolean containsEntityKey(EntityKey key) { - if ( key.isBatchLoadable() ) { - LinkedHashSet set = batchLoadableEntityKeys.get( key.getEntityName()); + if ( batchLoadableEntityKeys != null && key.isBatchLoadable() ) { + LinkedHashSet set = batchLoadableEntityKeys.get( key.getEntityName() ); if ( set != null ) { return set.contains( key ); } @@ -179,8 +190,14 @@ public class BatchFetchQueue { final Serializable id, final int batchSize, final EntityMode entityMode) { - Serializable[] ids = new Serializable[batchSize]; + + final Serializable[] ids = new Serializable[batchSize]; ids[0] = id; //first element of array is reserved for the actual instance we are loading! + + if ( batchLoadableEntityKeys == null ) { + return ids; + } + int i = 1; int end = -1; boolean checkForEnd = false; @@ -238,20 +255,27 @@ public class BatchFetchQueue { public void addBatchLoadableCollection(PersistentCollection collection, CollectionEntry ce) { final CollectionPersister persister = ce.getLoadedPersister(); - LinkedHashMap map = batchLoadableCollections.get( persister.getRole() ); - if ( map == null ) { - map = new LinkedHashMap<>( 16 ); - batchLoadableCollections.put( persister.getRole(), map ); + if ( batchLoadableCollections == null ) { + batchLoadableCollections = new HashMap<>( 12 ); } + + final LinkedHashMap map = batchLoadableCollections.computeIfAbsent( + persister.getRole(), + k -> new LinkedHashMap<>( 16 ) + ); + map.put( ce, collection ); } - + /** * After a collection was initialized or evicted, we don't * need to batch fetch it anymore, remove it from the queue * if necessary */ public void removeBatchLoadableCollection(CollectionEntry ce) { + if ( batchLoadableCollections == null ) { + return; + } LinkedHashMap map = batchLoadableCollections.get( ce.getLoadedPersister().getRole() ); if ( map != null ) { map.remove( ce ); @@ -271,9 +295,13 @@ public class BatchFetchQueue { final Serializable id, final int batchSize) { - Serializable[] keys = new Serializable[batchSize]; + final Serializable[] keys = new Serializable[batchSize]; keys[0] = id; + if ( batchLoadableCollections == null ) { + return keys; + } + int i = 1; int end = -1; boolean checkForEnd = false;