Диаграмма внутренней структуры JVM
Виртуальные машины Java в основном разделены на пять областей: область метода, куча, стек Java, регистр ПК и локальный стек. Давайте посмотрим на некоторые важные проблемы о структуре JVM.
1. Какие области разделены? Какие из них частные?
Стек Java, локальный стек методов и счетчик программы установлены и уничтожаются, когда пользовательский поток запускается и заканчивается.
Каждая поток имеет эти независимые области. Область метода и куча используются всеми потоками во всем процессе JVM.
2. Что сохраняется в области метода? Будет ли он переработан?
Область метода не только сохранена информация о методе и код. В то же время, в субрегионе, называемом постоянным пулом выполнения, различные символические ссылки в постоянной таблице в файле класса, а также переведены прямые ссылки. Эта информация доступна через объект класса в куче в качестве интерфейса.
Хотя информация о типе хранится в области метода, она также будет переработана, но условия утилизации относительно строгие:
(1) Все экземпляры этого класса были переработаны
(2) Загрузка класса, загружающий этот класс, был переработан
(3) Объект класса этого класса нигде не упоминается (включая доступ к классу.
3. Содержание постоянного пула в области метода неизменным?
Постоянный пул времени выполнения в области метода сохраняет данные в пуле статических постоянных в файле класса. В дополнение к хранению различных буквальных и символических ссылок, полученных во время компиляции, он также содержит переведенные прямые ссылки. Но это не означает, что постоянный пул не изменится во время выполнения. Например, при запуске вы можете вызвать метод стажировки строки и поместить новые константы строки в бассейн.
пакет com.cdai.jvm; public Class RuntimeConstantPool {public static void main (string [] args) {string s1 = new String ("hello"); String S2 = New String ("Hello"); System.out.println («До стажировки, S1 == S2:» + (S1 == S2)); s1 = s1.intern (); s2 = s2.intern (); System.out.println («После стажировки, s1 == s2:» + (s1 == s2)); }}
4. Все экземпляры объекта выделены на кучу?
Благодаря постепенной зрелости технологии анализа побега технологии распределения на стойке и скалярной оптимизации замены сделали «все объекты, выделенные на кучу» менее абсолютными.
Так называемый побег означает, что когда указатель объекта ссылается множеством методов или потоков, мы говорим, что этот указатель уходит.
Вообще говоря, объекты Java выделяются в куче, и только указатели объекта сохраняются в стеке. Предполагая, что локальная переменная не уходит во время выполнения метода (подвергается внешней стороне метода), она будет выделена непосредственно на стеке, а затем будет продолжать выполняться в стеке вызовов. После выполнения метода пространство стека перерабатывается, а локальные переменные также переработаны. Это уменьшает распределение большого количества временных объектов в куче и повышает эффективность утилизации GC.
Кроме того, анализ Escape также пропустит блокировки на локальных переменных, которые не сбежали, и опускают блокировки, принадлежащие этой переменной.
При включении метода анализа побега добавьте параметры запуска JVM: -xx: +Docapeanalysis? Escapeanalysistest.
5. Сколько способов получить доступ к объектам на куче?
(1) Прямой доступ к указателю
Ссылка на стеке сохраняет указатель на объект на куче, и вы можете найти объект за один раз, что имеет более высокую скорость доступа.
Однако, когда объекты перемещаются в куче (каждая объекты часто перемещаются во время сбора мусора), значение переменной указателя в стеке также необходимо изменить. В настоящее время JVM Hotspot принимает этот метод.
(2) косвенное доступ к ручке
Ссылка на стеке указывает на ручку в пуле ручки, и объект доступ к значению в этой ручке. Следовательно, ручка похожа на вторичный указатель, который требует двух позиционирования для доступа к объекту, что медленнее, чем прямое позиционирование указателя, но когда объект перемещается в куче, нет необходимости изменять значение, упомянутое в стеке.
Как переполнить JVM
Понимая роль пяти областей памяти виртуальных машин Java, давайте продолжать учиться при каких обстоятельствах, эти области будут переполнены.
1. Конфигурация параметров виртуальной машины
-Xms: начальный размер кучи, по умолчанию составляет 1/64 физической памяти (<1 ГБ); По умолчанию (параметр minheapfreeratio может быть скорректирован) Когда память свободной кучи составляет менее 40%, JVM увеличит кучу до максимального предела -xmx.
-Xmx: максимальный размер кучи, по умолчанию (параметр maxheapfreeratio может быть скорректирован) Когда память свободной кучи превышает 70%, JVM уменьшит кучу до минимального предела -xms.
-Xss: размер стека для каждого потока. После JDK5.0 размер стека каждого потока составляет 1 м, а в прошлом размер стека каждого потока составляет 256 тыс. Он должен быть скорректирован соответствующим образом в соответствии с необходимым размером памяти потока приложения. В той же физической памяти уменьшение этого значения может генерировать больше потоков. Тем не менее, операционная система по -прежнему имеет ограничение на количество потоков в процессе и не может быть получена бесконечно, со значениями опыта в диапазоне от 3000 до 5000. Как правило, небольшие приложения, если стек не очень глубокий, 128 тыс. Достаточно. Для больших приложений рекомендуется 256K. Этот вариант оказывает большое влияние на производительность и требует строгого тестирования.
-Xx: permsize: установить начальное значение постоянного поколения (пермин). Значение по умолчанию составляет 1/64 физической памяти.
-Xx: maxpermsize: установите максимальное значение постоянного поколения. 1/4 физической памяти.
2. Переполнение зоны метода
Поскольку область метода содержит соответствующую информацию класса, когда мы загружаем слишком много классов, область метода будет переполнена. Здесь мы пытаемся переполнить область метода двумя методами: динамический прокси и прокси -сервер JDK.
2.1 JDK Dynamic Proxy
Пакет com.cdai.jvm.overflow; Импорт java.lang.reflect.invocationHandler; импортировать java.lang.reflect.method; Импорт java.lang.reflect.proxy; public Class methodAreaOverflow {static interface oomiinterface {} статический класс oomobject реализует oomiinterface {} static class oomobject2 реализует oomiinterface {} public void main (string [] args) {final oomobject object = new Oomobject (); while (true) {oomiinterface proxy = (oomiinterface) proxy.newproxyinstance (thread.currentthread (). getContextClassLoader (), oomobject.class.getInterfaces (), новый vocationHandler () {@Override public Object Invoke (Object proxy, метод, объект, объект [] Args). System.out.println ("Interceptor1 работает"); System.out.println (proxy.getClass ()); System.out.println ("Proxy1:" + Proxy); OomiInterface proxy2 = (oomiInterface) proxy.newproxyinstance (thread.currentThread (). GetContextClassLoader (), oomobject.class.getInterfaces (), new InvocationHandler () {@Override public Object Invoke (Object Proxy, Method Method, Object [] Args) Throwable {System.out.out.out {System.out. работа "); return Method.invoke (Object, args);}}); System.out.println (proxy2.getClass ()); System.out.println ("Proxy2:" + Proxy2); }}} Хотя мы продолжаем называть метод Proxy.newinstance () для создания класса прокси, JVM не имеет переполнения памяти.
Каждый вызов генерирует различный экземпляр класса прокси, но объект класса класса прокси не изменился. Это прокси
Есть ли у класса кэш для объекта класса класса прокси? Конкретные причины будут подробно проанализированы в последующем «JDK Dynamic Proxy и Cglib».
2.2 Агент cglib
Cglib также будет кэшировать объект класса класса прокси, но мы можем настроить его, чтобы не кэшировать объект класса.
Таким образом, цель переполнения области метода может быть достигнута путем многократного создания прокси -классов.
Пакет com.cdai.jvm.overflow; импортировать java.lang.reflect.method; Импорт net.sf.cglib.proxy.enhancer; Импорт net.sf.cglib.proxy.methodinterceptor; Импорт net.sf.cglib.proxy.methodproxy; public Class methodaReaOverflow2 {static class oomobject {} public static void main (string [] args) {while (true) {Enhancer Enhancer = new Enhancer (); Enhancer.setSuperClass (oomobject.class); Enhancer.SetUseCache (false); Enhancer.setCallback (новый метод Interceptor () {@Override public Object Intercept (объект obj, метод метода, объект [] args, methodproxy proxy) бросает Throwable {return Method.invoke (obj, args);}}); Oomobject proxy = (oomobject) Enhancer.create (); System.out.println (proxy.getClass ()); }}}
3. Переполнение кучи
Переполнение кучи относительно проста. Вы можете переполнить кучу, создав большой объект массива.
Пакет com.cdai.jvm.overflow; Общедоступный класс HeapoverFlow {Private Static Final Int MB = 1024 * 1024; @Suppresswarnings («неиспользованный») public static void main (string [] args) {byte [] bigmemory = new Byte [1024 * mb]; }}
4. Stack Overflow
Переполнение стека также распространено. Иногда, когда рекурсивный вызов, который мы пишем, не имеет правильного условия завершения, метод будет продолжать восхищаться, глубина стека будет продолжать увеличиваться, и в конечном итоге будет происходить переполнение стека.
Пакет com.cdai.jvm.overflow; открытый класс stackoverflow {private static int stackdepth = 1; public static void Stackoverflow () {StackDepth ++; stackoverflow (); } public static void main (string [] args) {try {StackOverflow (); } catch (Exception e) {System.err.println ("Debound:" + StackDepth); e.printstacktrace (); }}}