Чтобы избежать онлайн G C проблем.
Фон
Исходная карта будет скопирована в поток (содержащийся в пуле потоков), и в этом потоке скопированная карта может быть обновлена и после обновления некоторые скопированные карты могут возвращаться к исходной карте .
Опытные соображения
В онлайн-условиях есть два типичных условия по сравнению с моей локальной книгой ma c:
- гораздо более эффективные серверы (процессор, память и IO)
- высокая пропускная способность (QPS на миллионном уровне)
Размер информации для origMap
может быть: 50 ключей, и каждое значение составляет около 50 символов.
Текущее решение
Сейчас я использую ThreadLocal
для создания ReusableMap
, чтобы гарантировать, что каждая карта связана с потоком, и когда требуется копия и карта в потоке уже создана, мы можем напрямую использовать карту.
Конечно, сначала нам нужно очистить карту и скопировать содержимое с исходной карты .
Я думал, что это уменьшит G C, но когда я запускаю некоторый тест с использованием jmh и отслеживаю результат в Visual G C через jvisualvm ; К сожалению, я обнаружил, что это не так, как я ожидал. Тем не менее, есть много GC, как и раньше.
Обновлено 2020-02-21
Во-первых, действительно благодаря помощи @Holger и @GotoFinal я пробовал другие варианты с моим ограниченным пониманием. Но пока, все так плохо, ничего не работает с моим локальным тестом.
Ничего полезного не получится, и я думаю, что попробую что-то другое, чтобы глубже изучить технологию оптимизации и кэширования JVM.
Просто для справки, тесты, которые я выполнил следующим образом:
- настройка размера карты в ключевом аспекте, размера значения в аспекте длины;
- использование простого огромного l oop путем удаления jmh для устранения скрывающихся влияний;
- использовать несколько карт (вместо одной - поскольку есть сценарии ios, нам нужно передать несколько);
- вносит ограничительные изменения в дочерний поток, чтобы поддерживать более высокое повторное использование в аспекте входа и узла при использовании
cacheMap.putAll(origMap)
cacheMap.keySet().retainAll(origMap.keySet())
- запускать тесты дольше от 10 минут до 2 ч 30 м;
Некоторый код, демонстрирующий то, что я только что упомянул:
public class ReusableHashMapTwoCopy {
private static final String DEFAULT_MAP_KEY = "defaultMap";
/**
* weak or soft reference perhaps could be used: https://stackoverflow.com/a/299702/2361308
* <p>
* via the static ThreadLocal initialized, each thread will only see the value it set itself;
*/
private static ThreadLocal<Map> theCache = new ThreadLocal<>();
/**
* the default usage when there is only one map passed from parent
* thread to child thread.
*
* @param origMap the parent map
* @param <K> generic type for the key
* @param <V> generic type for the value
* @return a map used within the child thread - the reusable map
*/
public static <K, V> Map<K, V> getMap(Map<K, V> origMap) {
return getMap(DEFAULT_MAP_KEY, origMap);
}
public static <K, V> Map<K, V> getMap() {
return getMap(DEFAULT_MAP_KEY);
}
/**
* clone the parent-thread map at the beginning of the child thread,
* after which you can use the map as usual while it's thread-localized;
* <p>
* no extra map is created for the thread any more - preventing us from creating
* map instance all the time.
*
* @param theMapKey the unique key to specify the map to be passed into the child thread;
* @param origMap the parent map
* @param <K> generic type for the key
* @param <V> generic type for the value
* @return the cached map reused within the child thread
*/
public static <K, V> Map<K, V> getMap(String theMapKey, Map<K, V> origMap) {
Map<String, Map<K, V>> threadCache = theCache.get();
if (Objects.isNull(threadCache)) {
// System.out.println("## creating thread cache");
threadCache = new HashMap<>();
theCache.set(threadCache);
} else {
// System.out.println("**## reusing thread cache");
}
Map<K, V> cacheMap = threadCache.get(theMapKey);
if (Objects.isNull(cacheMap)) {
// System.out.println(" ## creating thread map cache for " + theMapKey);
cacheMap = new HashMap<>();
} else {
// System.out.println(" **## reusing thread map cache for " + theMapKey);
cacheMap.clear();
}
if (MapUtils.isNotEmpty(origMap)) {
cacheMap.putAll(origMap);
cacheMap.keySet().retainAll(origMap.keySet());
}
threadCache.put(theMapKey, cacheMap);
return cacheMap;
}
public static <K, V> Map<K, V> getMap(String theMapKey) {
return getMap(theMapKey, null);
}
public static void main(String[] args) throws Exception {
org.openjdk.jmh.Main.main(args);
// print(MyState.parentMapMedium_0);
// print(MyState.parentMapSmall_0);
}
private static void blackhole(Object o) {
}
@Benchmark
@Fork(value = 1, warmups = 0, jvmArgs = {"-Xms50M", "-Xmx50M"})
@Warmup(iterations = 1, time = 5)
@Timeout(time = 3, timeUnit = TimeUnit.HOURS)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MINUTES)
@Measurement(iterations = 1, time = 150, timeUnit = TimeUnit.MINUTES)
public void testMethod() throws Exception {
final Map<String, String> theParentMap0 = MyState.parentMapSmall_0;
final Map<String, String> theParentMap1 = MyState.parentMapSmall_1;
// final Map<String, String> theParentMap0 = MyState.parentMapMedium_0;
// final Map<String, String> theParentMap1 = MyState.parentMapMedium_1;
ThreadUtils.getTheSharedPool().submit(() -> {
try {
Map<String, String> theChildMap0 = new HashMap<>(theParentMap0);
theChildMap0.put("test0", "child");
Map<String, String> theChildMap1 = new HashMap<>(theParentMap1);
theChildMap1.put("test1", "child");
for (int j = 0; j < 1_0; ++j) {
blackhole(theChildMap0);
blackhole(theChildMap1);
sleep(10);
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
}).get();
}
private static void print(Object o) {
print(o, "");
}
private static void print(Object o, String content) {
String s = String
.format("%s: current thread: %s map: %s", content, Thread.currentThread().getName(), toJson(o));
System.out.println(s);
}
@State(Scope.Benchmark)
public static class MyState {
// 20 & 100 -> 2.5k
static Map<String, String> parentMapSmall_0 = generateAMap(20, 100);
static Map<String, String> parentMapSmall_1 = generateAMap(20, 100);
// 200 & 200 -> 45k
static Map<String, String> parentMapMedium_0 = generateAMap(200, 200);
static Map<String, String> parentMapMedium_1 = generateAMap(200, 200);
}
private static Map<String, String> generateAMap(int size, int lenLimit) {
Map<String, String> res = new HashMap<>();
String aKey = "key - ";
String aValue = "value - ";
for (int i = 0; i < size; ++i) {
aKey = i + " - " + LocalDateTime.now().toString();
aValue = i + " - " + LocalDateTime.now().toString() + aValue;
res.put(aKey.substring(0, Math.min(aKey.length(), lenLimit)),
aValue.substring(0, Math.min(aValue.length(), lenLimit)));
}
return res;
}
}