|
22 | 22 | import org.elasticsearch.common.unit.ByteSizeUnit;
|
23 | 23 | import org.elasticsearch.common.unit.ByteSizeValue;
|
24 | 24 | import org.elasticsearch.common.unit.MemorySizeValue;
|
| 25 | +import org.elasticsearch.common.util.StringLiteralDeduplicator; |
25 | 26 | import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
|
26 | 27 | import org.elasticsearch.common.xcontent.XContentParserUtils;
|
27 | 28 | import org.elasticsearch.core.Booleans;
|
@@ -99,7 +100,27 @@ private static Settings of(Map<String, Object> settings, SecureSettings secureSe
|
99 | 100 |
|
100 | 101 | private Settings(Map<String, Object> settings, SecureSettings secureSettings) {
|
101 | 102 | // we use a sorted map for consistent serialization when using getAsMap()
|
102 |
| - this.settings = Collections.unmodifiableNavigableMap(new TreeMap<>(settings)); |
| 103 | + final TreeMap<String, Object> tree = new TreeMap<>(); |
| 104 | + for (Map.Entry<String, Object> settingEntry : settings.entrySet()) { |
| 105 | + final Object value = settingEntry.getValue(); |
| 106 | + final Object internedValue; |
| 107 | + if (value instanceof String) { |
| 108 | + internedValue = internKeyOrValue((String) value); |
| 109 | + } else if (value instanceof List) { |
| 110 | + @SuppressWarnings("unchecked") |
| 111 | + List<String> valueList = (List<String>) value; |
| 112 | + final int listSize = valueList.size(); |
| 113 | + final String[] internedArr = new String[listSize]; |
| 114 | + for (int i = 0; i < valueList.size(); i++) { |
| 115 | + internedArr[i] = internKeyOrValue(valueList.get(i)); |
| 116 | + } |
| 117 | + internedValue = org.elasticsearch.core.List.of(internedArr); |
| 118 | + } else { |
| 119 | + internedValue = value; |
| 120 | + } |
| 121 | + tree.put(internKeyOrValue(settingEntry.getKey()), internedValue); |
| 122 | + } |
| 123 | + this.settings = Collections.unmodifiableNavigableMap(tree); |
103 | 124 | this.secureSettings = secureSettings;
|
104 | 125 | }
|
105 | 126 |
|
@@ -418,7 +439,7 @@ public List<String> getAsList(String key, List<String> defaultValue, Boolean com
|
418 | 439 | if (valueFromPrefix instanceof List) {
|
419 | 440 | @SuppressWarnings("unchecked")
|
420 | 441 | final List<String> valuesAsList = (List<String>) valueFromPrefix;
|
421 |
| - return Collections.unmodifiableList(valuesAsList); |
| 442 | + return valuesAsList; |
422 | 443 | } else if (commaDelimited) {
|
423 | 444 | String[] strings = Strings.splitStringByCommaToArray(get(key));
|
424 | 445 | if (strings.length > 0) {
|
@@ -1222,11 +1243,19 @@ public boolean shouldRemoveMissingPlaceholder(String placeholderName) {
|
1222 | 1243 | }
|
1223 | 1244 | if (entry.getValue() instanceof List) {
|
1224 | 1245 | @SuppressWarnings("unchecked")
|
1225 |
| - final ListIterator<String> li = ((List<String>) entry.getValue()).listIterator(); |
| 1246 | + final List<String> mutableList = new ArrayList<>((List<String>) entry.getValue()); |
| 1247 | + final ListIterator<String> li = mutableList.listIterator(); |
| 1248 | + boolean changed = false; |
1226 | 1249 | while (li.hasNext()) {
|
1227 | 1250 | final String settingValueRaw = li.next();
|
1228 | 1251 | final String settingValueResolved = propertyPlaceholder.replacePlaceholders(settingValueRaw, placeholderResolver);
|
1229 |
| - li.set(settingValueResolved); |
| 1252 | + if (settingValueResolved.equals(settingValueRaw) == false) { |
| 1253 | + li.set(settingValueResolved); |
| 1254 | + changed = true; |
| 1255 | + } |
| 1256 | + } |
| 1257 | + if (changed) { |
| 1258 | + entry.setValue(org.elasticsearch.core.List.copyOf(mutableList)); |
1230 | 1259 | }
|
1231 | 1260 | continue;
|
1232 | 1261 | }
|
@@ -1478,4 +1507,18 @@ private static String toString(Object o) {
|
1478 | 1507 | return o == null ? null : o.toString();
|
1479 | 1508 | }
|
1480 | 1509 |
|
| 1510 | + private static final StringLiteralDeduplicator settingLiteralDeduplicator = new StringLiteralDeduplicator(); |
| 1511 | + |
| 1512 | + /** |
| 1513 | + * Interns the given string which should be either a setting key or value or part of a setting value list. This is used to reduce the |
| 1514 | + * memory footprint of similar setting instances like index settings that may contain mostly the same keys and values. Interning these |
| 1515 | + * strings at some runtime cost is considered a reasonable trade-off here since neither setting keys nor values change frequently |
| 1516 | + * while duplicate keys values may consume significant amounts of memory. |
| 1517 | + * |
| 1518 | + * @param s string to intern |
| 1519 | + * @return interned string |
| 1520 | + */ |
| 1521 | + static String internKeyOrValue(String s) { |
| 1522 | + return settingLiteralDeduplicator.deduplicate(s); |
| 1523 | + } |
1481 | 1524 | }
|
0 commit comments