Яма, вызванная JDK1.6 в производственной среде

Эта статья опубликована в сообществе 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, императивного языка программирования
{{о.имя}}
{{м.имя}}

рекомендация

отmy.oschina.net/u/4526289/blog/10092960
рекомендация