Эта статья опубликована в сообществе Huawei Cloud Community « [High Concurrency] Помните о яме, вызванной JDK1.6 в производственной среде! ", Автор: Ледник.
Недавно друг столкнулся с конфузом: у написанной им программы вообще нет проблем в тестовой среде, но часто происходит переполнение памяти при отправке в рабочую среду. Эта проблема беспокоит его уже больше недели.
Позже, в процессе устранения неполадок, я обнаружил, что JDK, используемый этим маленьким партнером, все еще был версии 1.6. Поначалу я не особо задумывался об этом и продолжал проверять написанный им код, но проблем не обнаружил. Но как только программа в производственной среде запущена, JVM не потребовалось много времени, чтобы выдать исключение переполнения памяти.
Это странно, что происходит?
Добавьте разумные параметры JVM при запуске программы, проблема все еще существует. . .
Ни в коем случае, продолжайте смотреть на его код! Случайно я обнаружил, что в коде, который он написал, метод substring() класса String широко использовался для перехвата строки. Итак, я следовал коду в JDK, чтобы проверить переданные параметры.
Это ненароком щелкнул, чтобы проверить, и нашел проблему! !
Яма класса String в JDK1.6
После анализа я обнаружил большую дыру в классе String в JDK1.6! Почему вы говорите, что это яма? Это потому, что его метод substring() сделает людей несчастными! Нечего сказать, давайте сначала рассмотрим метод substring() класса String в JDK1.6.
подстрока общедоступной строки (int bedinIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException (beginIndex); } if(endIndex > count){ throw new StringIndexOutOfBoundsException(endIndex); } if(beginIndex > endIndex){ бросить новое исключение StringIndexOutOfBoundsException(endIndex - beginIndex); } return ((beginIndex == 0) && (endIndex == count)) ? это: новая строка (смещение + beginIndex, endIndex - beginIndex, значение); }
Далее давайте рассмотрим метод построения класса String в JDK1.6, как показано ниже.
Строка (целое смещение, целое количество, значение char []) { this.value = значение; это.смещение = смещение; это.счет = количество; }
Смотрите, вот, я считаю, что внимательные друзья обнаружили проблему, и виновником проблемы является следующая строка кода.
это.значение = значение;
В JDK1.6 при использовании конструктора класса String для создания подстроки это не просто копия требуемого объекта, а каждый раз цитируется все значение. Если исходная строка относительно велика, даже если строка больше не используется, память, выделенная строкой, не будет освобождена. Это тоже вывод, к которому я пришел после долгого анализа кода, это действительно очень плохо! !
Теперь, когда проблема найдена, давайте исправим ее.
обновить JDK
Поскольку в классе String в JDK1.6 есть такая огромная яма, самый прямой и эффективный способ — обновить JDK. Итак, я объяснил ситуацию своему другу и попросил его обновить JDK до JDK1.8.
Точно так же давайте взглянем на метод substring() класса String в JDK1.8.
общественная подстрока String (int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException (beginIndex); } if (endIndex > value.length) { throw new StringIndexOutOfBoundsException(endIndex); } int subLen = endIndex - beginIndex; если (subLen < 0) { бросить новое исключение StringIndexOutOfBoundsException (subLen); } return ((beginIndex == 0) && (endIndex == value.length)) ? это : новая строка (значение, beginIndex, subLen); }
В методе substring() класса String в JDK1.8 метод построения класса String также вызывается для создания подстроки Давайте рассмотрим этот метод построения, как показано ниже.
общедоступная строка (значение символа [], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException (offset); } if (count <= 0) { if (count < 0) { throw new StringIndexOutOfBoundsException(count); } если (смещение <= значение.длина) { это.значение = "".значение; возвращаться; } } // Примечание: смещение или количество может быть около -1>>>1. если (смещение > значение.длина - количество) { бросить новое исключение StringIndexOutOfBoundsException (смещение + количество); } this.value = Arrays.copyOfRange(значение, смещение, смещение+количество); }
В JDK1.8, когда нам нужна подстрока, substring генерирует новую строку, которая создается функцией конструктора Arrays.copyOfRange. Это не проблема.
Оптимизация параметров запуска JVM
Здесь, чтобы лучше улучшить производительность системы, я также помог этому маленькому партнеру оптимизировать параметры запуска JVM.
С разрешения моих друзей я кратко перечислил масштабы их бизнеса и конфигурацию серверов: вся система использует распределенную архитектуру, а каждый бизнес-сервис в этой архитектуре использует кластерное развертывание со средним ежедневным объемом посещений в сотни миллионов, средней ежедневной транзакцией. порядка 50Вт~100Вт и порядка Каждый серверный узел системы сконфигурирован как 4-ядерный 8G. В настоящее время JDK обновлен до версии 1.8.
Согласно вышеперечисленным условиям я даю конфигурацию параметров после настройки JVM.
-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M
Что касается того, почему приведена вышеприведенная конфигурация параметров JVM, я напишу отдельную статью позже, чтобы специально проанализировать, как настраивать параметры JVM в соответствии с реальными бизнес-сценариями.
После анализа и решения проблемы программа маленького партнера работает без сбоев в производственной среде, по крайней мере, пока нет ситуации переполнения памяти! !
в заключение
Если в программе создается относительно большой объект, и мы генерируем какую-то другую информацию на основе этого большого объекта, в это время мы должны разорвать ссылочную связь с этим большим объектом, иначе скрытая опасность переполнения памяти будет скрыта.
Цель оптимизации JVM состоит в том, чтобы как можно больше выделять и перерабатывать объекты в новом поколении, стараться не допускать, чтобы слишком много объектов попадало в старое поколение слишком часто, избегать частой сборки мусора старого поколения и в то же время давать системе достаточный объем памяти, чтобы избежать частой сборки мусора нового поколения.
Нажмите, чтобы подписаться и узнать о свежих технологиях Huawei Cloud впервые~
Это бесконечно быстрее, чем Protocol Buffers. После десяти лет работы с открытым исходным кодом был наконец выпущен Cap'n Proto 1.0. Постдокторант Хуачжунского университета науки и технологий воспроизвел явление магнитной левитации LK-99. Лунсон Чжунке успешно разработал новое поколение процессора Loongson 3A6000 miniblink версии 108. Самое маленькое в мире ядро Chromium ChromeOS разделяет браузер и операционную систему на независимый твердотельный накопитель емкостью 1 ТБ в торговом центре Tesla China Mall по цене 2720 юаней. Huawei официально выпустила версию обновления безопасности HarmonyOS 4, в результате чего все приложения на основе Electron, чтобы заморозить AWS, начнут поддерживать общедоступные сетевые адреса IPv4 в следующем году Официальный выпуск Nim v2.0, императивного языка программирования