Skip to content

Commit b1e878f

Browse files
Release DocumentField instances in SearchHit.deallocate (#124925)
These can consume non-trivial heap if transport messages start to queue up. Let's release them asap.
1 parent a2d98e4 commit b1e878f

File tree

1 file changed

+22
-2
lines changed

1 file changed

+22
-2
lines changed

server/src/main/java/org/elasticsearch/search/SearchHit.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,13 +520,15 @@ public DocumentField removeDocumentField(String field) {
520520
* @return a map of metadata fields for this hit
521521
*/
522522
public Map<String, DocumentField> getMetadataFields() {
523+
assert hasReferences();
523524
return Collections.unmodifiableMap(metaFields);
524525
}
525526

526527
/**
527528
* @return a map of non-metadata fields requested for this hit
528529
*/
529530
public Map<String, DocumentField> getDocumentFields() {
531+
assert hasReferences();
530532
return Collections.unmodifiableMap(documentFields);
531533
}
532534

@@ -535,6 +537,7 @@ public Map<String, DocumentField> getDocumentFields() {
535537
* were required to be loaded. Includes both document and metadata fields.
536538
*/
537539
public Map<String, DocumentField> getFields() {
540+
assert hasReferences();
538541
if (metaFields.size() > 0 || documentFields.size() > 0) {
539542
final Map<String, DocumentField> fields = new HashMap<>();
540543
fields.putAll(metaFields);
@@ -556,6 +559,7 @@ public boolean hasLookupFields() {
556559
* Resolve the lookup fields with the given results and merge them as regular fetch fields.
557560
*/
558561
public void resolveLookupFields(Map<LookupField, List<Object>> lookupResults) {
562+
assert hasReferences();
559563
if (lookupResults.isEmpty()) {
560564
return;
561565
}
@@ -585,6 +589,7 @@ public void resolveLookupFields(Map<LookupField, List<Object>> lookupResults) {
585589
* A map of highlighted fields.
586590
*/
587591
public Map<String, HighlightField> getHighlightFields() {
592+
assert hasReferences();
588593
return highlightFields == null ? emptyMap() : highlightFields;
589594
}
590595

@@ -724,6 +729,17 @@ private void deallocate() {
724729
r.decRef();
725730
}
726731
SearchHit.this.source = null;
732+
clearIfMutable(documentFields);
733+
clearIfMutable(metaFields);
734+
this.highlightFields = null;
735+
}
736+
737+
private static void clearIfMutable(Map<String, DocumentField> fields) {
738+
// check that we're dealing with a HashMap, instances read from the wire that are empty be of an immutable type
739+
assert fields instanceof HashMap<?, ?> || fields.isEmpty() : fields;
740+
if (fields instanceof HashMap<?, ?> hm) {
741+
hm.clear();
742+
}
727743
}
728744

729745
@Override
@@ -756,12 +772,16 @@ public SearchHit asUnpooled() {
756772
innerHits == null
757773
? null
758774
: innerHits.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().asUnpooled())),
759-
documentFields,
760-
metaFields,
775+
cloneIfHashMap(documentFields),
776+
cloneIfHashMap(metaFields),
761777
ALWAYS_REFERENCED
762778
);
763779
}
764780

781+
private Map<String, DocumentField> cloneIfHashMap(Map<String, DocumentField> map) {
782+
return map instanceof HashMap<String, DocumentField> hashMap ? new HashMap<>(hashMap) : map;
783+
}
784+
765785
public boolean isPooled() {
766786
return refCounted != ALWAYS_REFERENCED;
767787
}

0 commit comments

Comments
 (0)