С этим вопросом на собеседовании столкнулся друг во время интервью. Когда будет выдано исключение OutOfMemery? На первый взгляд вроде бы все просто, но на самом деле расследование идет о понимании всей JVM, и этот вопрос можно превратить в какие-то сумбурные ответы из интернета, на самом деле есть в основном 4 сценария, которые можно резюмировал.
переполнение кучи памяти
Переполнение памяти кучи слишком распространено, и большинство людей должно быть в состоянии себе это представить. Память кучи используется для хранения экземпляров объектов. Нам нужно только продолжать создавать объекты и обеспечивать доступный путь между корнями GC и объектами, чтобы избежать сборки мусора. , то это исключение может возникнуть вскоре после того, как количество объектов превысит максимальный размер кучи.
Напишите фрагмент кода для его проверки и установите размер кучи на 2M.
public class HeapOOM {
public static void main(String[] args) {
List<HeapOOM> list = new ArrayList<>();
while (true) {
list.add(new HeapOOM());
}
}
}
Запустите код, и вскоре вы увидите исключение OOM, подсказка здесьJava heap spaceПереполнение кучи памяти.
Общий метод устранения неполадок можно проанализировать, установив параметр -XX: +HeapDumpOnOutOfMemoryError для вывода текущего моментального снимка дампа памяти при возникновении исключения.Анализ можно проанализировать с помощью Eclipse Memory Analyzer (MAT), а независимые файлы можно проанализировать вОфициальный сайтскачать.
Кроме того, если вы используете IDEA, вы можете использовать коммерческую версию JProfiler или версию JVM-Profiler с открытым исходным кодом.Кроме того, IDEA 2018 имеет встроенные инструменты анализа, включая функции Flame Graph и Call Tree.
Область метода (константный пул времени выполнения) и переполнение метапространства
Область метода, как и куча, представляет собой область, совместно которой поделите потоками, включая информацию о файле класса, постоянный пул времени выполнения и постоянный пул. Основное различие между постоянным пулом выполнения и постоянным бассейном является то, что он динамичен, то есть Не должен быть в файле класса. Только контент в постоянном пуле может ввести пул постоянного времени выполнения, а новые константы также могут быть помещены в пул во время выполнения, такими как метод стажера ().
Мы написали кусок кода для тестирования String.Intern (), мы установили -xx: MetaSpacesize = 50m -xx: MaxmetaSpacesize = 50 м юаней пространства. Поскольку я использую версию 1,8 JDK, версии 1.8 и предыдущий метод существует в регионе от имени постоянного (Permgen), концепции постоянного отменена после 1,8 поколения, в космический юань (MetAspace), если предыдущая версия Может быть навсегда установить Разместить размер генерации Maxermsize.
private static String str = "test";
public static void main(String[] args) {
List<String> list = new ArrayList<>();
while (true){
String str2 = str + str;
str = str2;
list.add(str.intern());
}
}
Запустите код, и вы обнаружите, что код сообщает об ошибке.
Снова измените конфигурацию, удалите ограничение метапространства, измените размер памяти кучи -Xms20m -Xmx20m, и вы увидите ошибку памяти кучи.
Почему это? intern() сам по себе является нативным методом, его функция такова: если пул строковых констант уже содержит строку, равную этому объекту String, он возвращает объект String, представляющий строку в пуле; в противном случае этот объект String содержит добавленную строку в пул констант, и возвращается ссылка на объект String.
После версии 1.7 пул строковых констант был перенесен в область кучи, поэтому будет выдаваться ошибка переполнения памяти кучи.Если версия до 1.7, вы увидите ошибку пространства PermGen.
прямое переполнение памяти
Прямая память не является частью области данных времени выполнения виртуальной машины и не ограничена памятью кучи, но ограничена размером памяти машины. Например, в NIO нативные функции могут использоваться для прямого выделения памяти вне кучи, что может легко привести к проблемам с OOM.
Прямой размер памяти можно указать с помощью -xx: maxdirectMemorySize, если он не указан, значение по умолчанию совпадает с размером кучи Java до -XMX.
Очевидная особенность переполнения памяти, вызванного прямой памятью, заключается в том, что в файле дампа не будет видно явных исключений.Если вы обнаружите, что файл дампа после OOM мал, и программа прямо или косвенно использует NIO, вы можете рассмотреть его проверку. Является ли это причиной.
переполнение стека
Стек является частным потоком, и его жизненный цикл такой же, как у потока. Когда выполняется каждый метод, создается фрейм стека для хранения такой информации, как таблица локальных переменных, стек операндов, динамическая ссылка, выход из метода и т. д. Процесс вызова метода — это процесс стекирования фреймов стека и стекирования.
В спецификации виртуальной машины Java определены два исключения для стека виртуальной машины:
- Если глубина стека, запрошенная потоком, больше глубины, разрешенной виртуальной машиной, будет выдано исключение StackOverflowError.
- Если стек виртуальной машины может быть динамически расширен, и во время расширения не может быть применено достаточно памяти, создается исключение OutOfMemoryError.
Сначала напишите кусок кода для тестирования, установите -Xss160k, -Xss представляет размер памяти стека для каждого потока.
public class StackOOM {
private int length = 1;
public void stackTest() {
System.out.println("stack lenght=" + length);
length++;
stackTest();
}
public static void main(String[] args) {
StackOOM test = new StackOOM();
test.stackTest();
}
}
Откройте для себя тест, независимо от того, как установлены параметры однопоточного исключения StackOverflow.
Попробуйте изменить код так, чтобы он был многопоточным, и настройте -Xss2m, потому что чем больше памяти, выделенной для каждого потока, тем меньшее количество потоков может вместить пространство стека, и тем легче будет генерировать переполнение памяти. Наоборот, если памяти не хватает, этот параметр можно уменьшить для поддержки большего количества потоков.
public class StackOOM {
private void dontStop() {
while (true) {
}
}
public void stackLeakByThread() {
while (true) {
new Thread(() -> dontStop()).start();
}
}
public static void main(String[] args) throws Throwable {
StackOOM stackOOM = new StackOOM();
stackOOM.stackLeakByThread();
}
}