Руководство по виртуальной машине JVM

Java задняя часть
Руководство по виртуальной машине JVM

Прежде всего, поделитесь всеми предыдущими статьями, ставьте лайки, добавляйте в избранное и пересылайте три раза подряд. >>>>😜😜😜
Сборник статей:🎁nuggets.capable/post/694164…
Github :👉github.com/black-ant
Резервное копирование CASE:👉git ee.com/ant black/wipe…

предисловие

Некоторое время назад, когда я посмотрел на богатство, которое накопил за эти годы, я вдруг почувствовал удовлетворение. Но подумав о тяготах поиска информации за эти годы, я решил разобраться в этих документах и ​​поделиться ими со всеми. Заметки кричащие, в них могут быть неточности, исправления приветствуются. Я также хотел бы поблагодарить преданных за их преданность.Документы временно разделены на несколько:

Кроме того, есть и другие заметки, которые будут опубликованы и обработаны одна за другой, спасибо за вашу поддержку.

1. Базовые знания

1.1 Переполнение памяти

1.2 Разделение нитей системы

// 串行收集器
    : 用单线程处理所有垃圾回收工作 , 效率高
    : 数据量比较小(100M左右);单处理器下并且对响应时间无要求的应用
// 并行收集器
    : “对吞吐量有高要求”,多CPU、对应用响应时间无要求的中、大型应用
	
// 并发处理器:
    : 对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用

1.3 Четыре ссылочных типа в Java

  • сильное цитирование(StrongReference): обычно создается непосредственно для new , пока есть объекты, указывающие на ,без переработки
  • Мягкие ссылки (SoftReference): слабая ссылка будет очищена до переполнения памяти, и если она все еще существует, OutofMemory (extends WeakReference)
  • слабая ссылка(WeakReference): будет существовать только до следующего жизненного цикла, SoftReference (new SoftReference(res))
  • фантомные ссылки (PhantomReference): Основная функция — получение уведомления во время сборки мусора
// 软引用创建案例一 : 
String str = new String("abc"); 
SoftReference<String> softReference = new SoftReference<String>(str);

// 软引用创建案例二 : 
ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
String str = new String("abc");
SoftReference<String> softReference = new SoftReference<>(str, referenceQueue);

softReference.get()
referenceQueue.poll()

// 弱引用创建案例: 
WeakReference<String> weakReference = new WeakReference<>(str);

// 虚引用创建案例 : 
ReferenceQueue queue = new ReferenceQueue(); // 创建虚引用,要求必须与一个引用队列关联 
PhantomReference pr = new PhantomReference(str, queue);

1.4 Способ выделения памяти

Существует 2 способа выделения памяти:

  • столкновение указателя: Регулировка памяти, переключатель перемещения указателя для выделения памяти
  • бесплатный список: сохранить список доступной памяти, получить ее из списка

1.5 TLAB

Что такое ТЛАБ?

Полное название TLAB:Thread Local Allocation Buffer,СейчасРаспределение локального кеша потока, которая представляет собой область выделения памяти для конкретного потока. Если задан параметр виртуальной машины -XX:UseTLAB, при инициализации потока он также будет применяться для памяти указанного размера, которая используется только текущим потоком, так что каждый поток имеет отдельное пространство. нужно выделить память, вы можете использовать ее самостоятельно. Таким образом, нет конкуренции, что может значительно повысить эффективность выделения.

В потоке кода объект указывает на фактическое пространство памяти по ссылке, а указатель является соответствующим указателем.

В динамической памяти кусок памяти делится на 2 указателем,Левая часть — это выделенное пространство памяти, правая часть пуста , Каждый раз, когда создается новый объект, указатель перемещается вправо на расстояние размера объекта, что называется столкновением указателя..但是当多线程高并发情况下 , 会出现指针来不及修改的情况

Память пространства TLAB очень мала.По умолчанию она занимает всего 1% от всего пространства Eden.Вы также можете использовать опцию- XX:TLABWasteTargetPercentУстановите процентный размер пространства Eden, занимаемого пространством TLAB.

Суть TLAB на самом деле представляет собой область, управляемую тремя указателями:начало, начало и конец, каждый поток выделит пространство из Eden, например, 100 КБ, как свой собственный TLAB, где начало и конец используются в качестве заполнителей для обозначения области, управляемой этим TLAB в Eden, и пространство в Eden не будет заблокировано. другие темы приходят сюда, чтобы выделить.

TLAB только позволяет каждому потоку иметь собственный указатель выделения, но пространство памяти для хранения объектов по-прежнему доступно для всех потоков, но другие потоки не могут выделять в этой области.

Недостатки TLAB

  1. Размер пространства TLAB фиксирован, но в это время большой объект, остальное пространство моего TLAB больше не может вместить его. (Например, 100 КБ TLAB поставляется с объектом 110KB)
  2. Осталось еще немного места TLAB, что немного неохотно.
  3. Когда в Eden будет достаточно места, вы можете снова подать заявку на TLAB. У меня недостаточно места. GC начнется в Eden области Heap.
  4. TLAB позволяет тратить пространство впустую, что приводит к прерывистому пространству в районе Эдема. Мне понадобится кто-нибудь, чтобы помочь позже.

2. Виртуальная машина

2.1 Виртуальная машина Java

Виртуальная машина Java, виртуальная машина может выполнять процесс байт-кода Java, который позволяет выполнять любой запрос Java с использованием нескольких платформ, но представляет собой кроссплатформенные программы Java (включая файл байт-кода), а не JVM.

// 内存区
- 将内存划分成若干个区以模拟实际机器上的存储、记录和调度功能模块
- 如实际机器上的各种功能的寄存器或者 PC 指针的记录器等。

// 执行引擎
执行引擎的任务是负责执行 class 文件中包含的字节码指令,相当于实际机器上的 CPU 。

// 本地方法调用
调用 C 或 C++ 实现的本地方法的代码返回结果。

// 类加载器
在 JVM 启动时或者类运行时将需要的 class 加载到JVM中

> 运行时数据区=====================================================
// 程序计数器
Java 线程私有,类似于操作系统里的 PC 计数器,它可以看做是当前线程所执行的字节码的行号指示器。
- 如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址
- 如果正在执行的是 Native 方法,这个计数器值则为空(Undefined)
    
// 虚拟机栈(栈内存)
Java线程私有,虚拟机栈描述的是 Java 方法执行的内存模型
    
// 本地方法栈 
和 Java 虚拟机栈的作用类似,区别是该区域为 JVM 提供使用 Native 方法的服务
    
    
// 堆内存(线程共享)
所有线程共享的一块区域,垃圾收集器管理的主要区域
- 每个方法在执行的时候,都会创建一个栈帧用于存储局部变量、操作数、动态链接、方法出口等信息。
- 每个方法调用都意味着一个栈帧在虚拟机栈中入栈到出栈的过程。
    
// 方法区(线程共享)
各个线程共享的一个区域,用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
- 线程共享区域,因此这是线程不安全的区域。
- 方法区也是一个可能会发生OutOfMemoryError的区域。
- 方法区存储的是从Class文件加载进来的静态变量、类信息、常量池以及编译器编译后的代码。
    

2.2 Дата памяти кучи

// 内存堆特点
- 存储的是我们new来的对象,不存放基本类型和对象引用。
- 由于创建了大量的对象,垃圾回收器主要工作在这块区域。
- 线程共享区域,因此是线程不安全的。
- 能够发生内存溢出,主要有OutOfMemoryError和StackOverflowError。
    
// 分代
Java堆区还可以划分为新生代和老年代,新生代又可以进一步划分为Eden区、Survivor 1区、Survivor 2区    
    
// 注意比例 : 
 8:1:1 + 2:3    

2.3 Детали стека памяти

  • Потоко-частная область, каждый поток имеет свой собственный стек виртуальной машины, так что это потокобезопасная область.
  • Хранит основные типы данных и ссылки на объекты.
  • При выполнении каждого метода в стеке виртуальной машины будет создан соответствующий фрейм стека, а после выполнения метода фрейм стека будет уничтожен.
  • Фрейм стека методов — это стек виртуальной машины в порядке поступления. Каждый кадр стека можно разделить на таблицу локальных переменных, стек операндов, динамическую ссылку, выход метода и дополнительную дополнительную информацию.
  • В этой области могут быть два исключения: если глубина стека, запрошенная потоком, превышает глубину, разрешенную виртуальной машиной, исключение, исключение в стойке блокировки (обычно вызвано рекурсией); исключение OutofMemoryError будет брошено, когда JVM не может подать заявку на достаточно памяти, когда динамически расширяется.

2 . 4 Куча памяти Java и разница в стеке

  • стек памяти: ссылочные переменные, используемые для хранения переменных примитивных типов и объектов.
  • куча памяти: используется для хранения объектов в Java, будь то переменные-члены, локальные переменные или переменные класса, объекты, на которые они указывают, хранятся в куче памяти.

  • Память стека принадлежит одному потоку, каждый поток будет иметь стековую память, и хранящиеся в нем переменные можно увидеть только в том потоке, которому он принадлежит, то есть память стека можно понимать как частную память потока;
  • Объекты в стеке памяти видны всем потокам. К объектам в стеке могут обращаться все потоки.

  • Если в памяти стека нет свободного места для хранения вызовов методов и локальных переменных, JVM выдаст ошибку java.lang.StackOverFlowError;
  • Если в памяти кучи нет свободного места для хранения сгенерированного объекта, JVM выдаст ошибку java.lang.OutOfMemoryError.

Память стека намного меньше памяти кучи, и если вы используете рекурсию, ваш стек быстро заполнится. Параметр -Xss устанавливает размер памяти стека, а параметр -Xms устанавливает размер в начале кучи.

2.6 Виртуальная машина HotSpot

Виртуальная машина HotSpot физически разделена на 2 части:

молодое поколение

  • Большинство вновь созданных объектов будут выделены здесь
  • Этот процесс исчезновения объектов из области того, что мы называем «малым ГК»

  • Кайнозойские три пространства
    • Эдемское пространство (Эдем)
    • Два места для выживших (Выживший)

Эдемский садSurvivor-ASurvivor-BOldсоздание объектапервый GCОбъекты продолжают накапливатьсяПереход на второе место по алгоритмуПродолжайте накапливать, GCЧерез несколько раундов остальные отправляются в старостьЭдемский садSurvivor-ASurvivor-BOld

описание объекта

// 老年代(old generation)
- 对象没有变得不可达,并且从新生代中存活下来,会被拷贝到这
- 对象从老年代中消失的过程,我们称之为”major GC“(或者”full GC“)

// card table
- 存在于老年代 ,512 byte,记录老年代对新生代的应用
- 由一个 write barrier

// 持久代( permanent generation ) 
- 又名方法区(method area)
- 保存类常量以及字符串常量

Способы ускорить выделение кеша

  • bump-the-pointer
    • Последние объекты в пространстве, чтобы создать сад Eden, сверху, в следующий раз, когда вы создаете объекты, чтобы найти
  • TLAB (локальные буферы распределения потоков)
    • Эта схема выделяет эксклюзивное пространство для каждого потока в пространстве Eden, так что каждый поток получает доступ только к своему собственному пространству TLAB, а в сочетании с технологией bump-the-pointer может выделять память без блокировки.

3. Уборка мусора

3.1 Причины сбора мусора

Программисты не могут автоматически завершить системный сборщик мусора, сборщик мусора обычно создается в следующих средах.

  • Большинство объектов быстро станут недоступными
  • Отсылок к новым объектам из старых объектов (объектов, которые создавались давно) очень мало.

3.2 Концепции сборки мусора

// stop-the-world
: Stop-the-world会在任何一种GC算法中发生
: Stop-the-world意味着 JVM 因为要执行GC而停止了应用程序的执行
: 当Stop-the-world发生时,除了GC所需的线程以外,所有线程都处于等待状态,直到GC任务完成
: GC优化很多时候就是指减少Stop-the-world发生的时间
   
    
   
// 为什么垃圾回收要分代: 
- 不同的对象生命周期是不一样的 ,采用不同的收集方式,可以提高回收率
  
// 分代的方式 :
- 年轻代
- 老年代
- 持久代 : 用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响

// 新生代 GC 和老年代 GC
新生代 : 一个 Eden 区 + 两个 Survivor 区
老年代 : 默认新生代(Young)与老年代(Old)的比例的值为 1:2 , 默认的 Eden:from:to=8:1:1
    
新生代GC(MinorGC/YoungGC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具备朝生夕灭的特性,所以 MinorGC 非常频繁,一般回收速度也比较快。
老年代GC(MajorGC/FullGC):指发生在老年代的 GC,出现了 MajorGC,经常会伴随至少一次的 MinorGC(但非绝对的,在 Parallel Scavenge 收集器的收集策略里就有直接进行 MajorGC 的策略选择过程)。MajorGC 的速度一般会比 MinorGC 慢 10 倍以上。

      
// 触发分代回收的方式
Scavenge GC 和 Full GC
- Scavenge GC : 新对象生成  , 并且在 Eden 申请空间失败 ,即触发   
- 
    
    
// 垃圾收集器
> 新生代收集器
    - Serial 收集器
    - ParNew 收集器
        ?- ParNew 收集器,是 Serial 收集器的多线程版。
    - Parallel Scavenge 收集器

> 老年代收集器
    - Serial Old 收集器
        ?- Serial Old 收集器,是 Serial 收集器的老年代版本。
    - Parallel Old 收集器
        ?- Parallel Old 收集器,是 Parallel Scavenge 收集器的老年代版本。
    - CMS 收集器

> 新生代 + 老年代收集器
    - G1 收集器
    - ZGC 收集器    
    
    
//  G1 和 CMS 的区别
• CMS :并发标记清除。他的主要步骤有:初始收集,并发标记,重新标记,并发清除(删除)、重置。
• G1:主要步骤:初始标记,并发标记,重新标记,复制清除(整理)
• CMS 的缺点是对 CPU 的要求比较高。G1是将内存化成了多块,所有对内段的大小有很大的要求。
• CMS是清除,所以会存在很多的内存碎片。G1是整理,所以碎片空间较小。
• G1 和 CMS 都是响应优先把,他们的目的都是尽量控制 STW 时间。
• G1 和 CMS 的 Full GC 都是单线程 mark sweep compact 算法,直到 JDK10 才优化为并行的。

коллекционер Последовательный, параллельный или параллельный Новое поколение/Старое поколение алгоритм Цель Применимая сцена
Serial сериал Кайнозой алгоритм репликации Приоритет скорости отклика Клиентский режим в однопроцессорной среде
Serial Old сериал старость Отметить-организовать Приоритет скорости отклика Режим клиента и план резервного копирования CMS в среде с одним ЦП
ParNew параллельно Кайнозой алгоритм репликации Приоритет скорости отклика Взаимодействие с CMS в режиме сервера в многопроцессорной среде
Parallel Scavenge параллельно Кайнозой алгоритм репликации приоритет пропускной способности Задачи, которые работают в фоновом режиме без особого взаимодействия
Parallel Old параллельно старость Отметить-организовать приоритет пропускной способности Задачи, которые работают в фоновом режиме без особого взаимодействия
CMS параллелизм старость отметка-ясно Приоритет скорости отклика Java-приложения сосредоточены на интернет-сайтах или системных серверах B/S.
G1 параллелизм both Mark-Organize + Algorithm Copy Приоритет скорости отклика Серверно-ориентированное приложение, заменяющее CMS в будущем

Настроить сборщик мусора

XX:+UseSerialGC:设置串行收集器
XX:+UseParallelGC::设置并行收集器
XX:+UseParalledlOldGC:设置并行年老代收集器
XX:+UseConcMarkSweepGC:设置并发收集器
XX:+UseG1GC:G1收集器,Java9默认开启,无需设置

Установить размер сборщика мусора

-XX:NewSize:设置年轻代最小空间大小
-XX:MaxNewSize:设置年轻代最大空间大小
-XX:PermSize:设置永久代最小空间大小
-XX:MaxPermSize:设置永久代最大空间大小
-XX:NewRatio:设置年轻代和老年代的比值。默认值-XX:NewRatio=2,表示年轻代与老年代比值为1:2,年轻代占整个堆大小的1/3
-XX:SurvivorRatio:设置年轻代中Eden区Survivor区的容量比值。默认值-XX:SurvivorRatio=8,表示Eden : Survivor0 : Survivor1 = 8 : 1 : 1

3.3 Общие сборщики мусора

3 .3 .1 Серийный коллекционер

Последовательный сборщик является самым простым и старым сборщиком, который приостанавливает все рабочие потоки во время сборки мусора до тех пор, пока процесс сборки мусора не завершится. Ниже приведена схема работы сборщика мусора Serial:

3 .3 .2 Коллекционер ParNew

ParNew 垃圾收集器实则是Serial 垃圾收集器的多线程版本,这个多线程在于ParNew垃圾收集器可以使用多条线程进行垃圾回收。

3 .3 .3 Параллельный сборщик мусора

Сборщик мусора Parallel Scavenge также называют сборщиком пропускной способности, поскольку он тесно связан с пропускной способностью.

Цель :То есть сократить время сборки мусора (то есть время сборки мусора короткое, но количество сборок велико), чтобы пользовательский код мог работать дольше.

3. 3 .4 Серийный старый коллекционер

Serial Old 收集器是Serial 收集器的老年代版本。其垃圾收集器的运行原理和Serial 收集器是一样的。

3 .3 .5 Параллельный старый коллектор

Parallel Old 收集器同样是Parallel Scavenge 收集器的老年代版本,支持多线程并发收集。

3 .3 .6 Сборщик CMS

CMS 垃圾收集器的运作过程相对前面几个垃圾收集器来说比较复杂,整个过程可以分为四个部分:

初始标记: 需要Stop The World,这里仅仅标记GC Roots能够直接关联的对象,所以速度很快。

并发标记: 从关联对象遍历整个GC Roots的引用链,这个过程耗时最长,但是却可以和用户线程并发运行。

重新标记: 修正并发时间,因为用户线程可能会导致标记产生变动,同样需要Stop The World。

并发清除: 清除已经死亡的对象。


3 .3 .7 Первый сборщик мусора

Сборщик Garbage First (сокращенно G 1) — это знаковое достижение в истории развития сборщиков мусора, в основном для серверных приложений. Кроме того, хотя коллектор G 1 по-прежнему сохраняет понятия нового поколения и старого поколения, новое поколение и старое поколение не являются фиксированными, они представляют собой динамическую коллекцию ряда регионов.

  • Параллелизм и параллелизм: G1 может в полной мере использовать аппаратные преимущества в многопроцессорной и многоядерной среде и использовать несколько ЦП для сокращения времени паузы Stop-The-World.Некоторым другим сборщикам изначально необходимо приостанавливать действия GC, выполняемые Java. Сборщик G1 по-прежнему может передавать данные Concurrency позволяет программам Java продолжать выполнение.
  • Коллекция поколений: хотя G1 может независимо управлять всей кучей GC без сотрудничества с другими сборщиками, он может обрабатывать вновь созданные объекты и объекты, которые сохранились в течение определенного периода времени и пережили несколько GC, по-разному, чтобы получить лучший эффект сбора.
  • пространственная интеграция: G1 — это сборщик, основанный на алгоритме «пометить-сортировать» в целом, и на основе алгоритма «копирования» локально (между двумя областями), что означает, что во время операции G1 не будет генерироваться пространство памяти. Фрагменты, которые могут обеспечить обычная доступная память после сбора.
  • предсказуемая пауза: Это еще одно большое преимущество G1 перед CMS.

3.4 Распространенные методы сборки мусора

// 方式一 : 调用 system gc 方法 , 开发者手动调用该命令 , 触发 gc
System.gc()
    
// 方式二 : 调用 Runtime.getRuntime().gc() 方式 , 该方法实际上会 invoke system.gc()
 Runtime.getRuntime().gc()
    
// 方式三 : Use jmap to force GC , 通过 jmap 命令执行 gc
// 该命令不能保证万无一失 , 如果 JVM 被占用导致 GC 无法执行会出现异常    
jmap -histo:live 7544

// 方式四 : 使用 Jcmd 命令执行 GC
// 通过 Java diagnostic command (JCMD) JVM 诊断命令触发 GC    
jcmd 7544 GC.run
    
// 方式五 : Use JConsole or Java Mission Control

3.5 Алгоритмы сборки мусора

// > 应用计数 
    : 对一个对象有引用/移除 。 即添加/删除数量 , 垃圾回收会回收数量为 0 的对象
    
// > 标记清除 
    : 第一阶段从引用根节点开始标记所有被引用的对象
    : 第二阶段遍历整个堆,把未标记的对象清除

// > 复制(Copying)
    : 将算法的内存空间分为相等的两个部分,回收时,遍历当前区域,将使用的对象复制到另外的区域

// > 标记-整理(Mark-Compact):
    : 第一阶段从根节点开始标记所有被引用对象
    : 第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放

4. Создание объекта

4.1 Процесс создания

1) Обнаружение того, загружен ли класс

Когда виртуальная машина сталкивается с новой инструкцией, сначала проверяет, может ли параметр этой инструкции находить символическую ссылку класса в постоянном пуле и проверяет, был ли класс, представленный этой символической ссылкой, был загружен, разрешен и инициализирован. Если нет, выполняется процесс загрузки класса.

2) Выделить память для объекта

После загрузки класса виртуальная машина начинает выделять память под объект, и определяется размер требуемой памяти. Просто выделите необходимую память в куче.

Существуют две особые ситуации для выделения памяти:

  • В первом случае объем памяти абсолютно регулярен.
  • Второй случай заключается в том, что пространство памяти не является непрерывным.
    • В случае с абсолютно обычной памятью все относительно просто, виртуальной машине нужно лишь переместить указатель между занятой памятью и доступным пространством, этот метод называется «коллизией указателей».
    • Это немного сложнее для неравномерного использования памяти, когда виртуальная машина должна поддерживать список доступной памяти. При выделении памяти нужно найти свободное место в памяти, а затем записать выделенную память в список, этот метод называется «свободный список».

Когда несколько потоков работают параллельно, будет казаться, что память выделяется объекту А, и прежде чем он успеет изменить указатель, объект Б использует этот указатель для выделения памяти, поэтому возникает проблема. Есть два решения этой проблемы:

  • Первый заключается в использовании синхронного подхода и использовании CAS для обеспечения атомарности операции.
  • Другой заключается в том, что каждый поток выделяет память в своем собственном пространстве, то есть каждый поток предварительно выделяет небольшой кусок памяти в куче, который называется локальным буфером выделения потока (TLAB). TLAB, не мешая друг другу. Это можно определить параметром -XX:+/-UseTLAB.

3) Инициализировать нулевое значение для выделенного пространства памяти

После завершения выделения памяти объекта также необходимо инициализировать пространство памяти объекта до нуля, чтобы объект можно было использовать непосредственно даже если это не рейтинг.

4) Сделайте другие настройки для объекта

После выделения места в памяти и инициализации нулевого значения виртуальной машине также необходимо произвести другие необходимые настройки объекта.Настройки находятся в заголовке объекта, в том числе класс, к которому относится объект, метаданные информации о классе, хэш-код объекта, оценка GC, возраст и т. д.

5) Выполните метод инициализации

После выполнения вышеуказанных шагов, даже если объект успешно создан на виртуальной машине, ему необходимо выполнить метод init, чтобы программа Java была действительно создана, потому что в это время объект только инициализируется с нулевым значением, и существует никакого реального создания.Начальное значение выделяется в соответствии с кодом в программе.После вызова метода init объект действительно можно использовать.

4.2 Структура памяти

对象的内存布局包括三个部分:
    - 对象头:对象头包括两部分信息。
        • 第一部分,是存储对象自身的运行时数据,如哈希码,GC 分代年龄,锁状态标志,线程持有的锁等等。
        • 第二部分,是类型指针,即对象指向类元数据的指针。
    - 实例数据:就是数据。
    - 对齐填充:不是必然的存在,就是为了对齐。

4.3 Доступ к позиционированию объектов

句柄定位:Java 堆会画出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。

直接指针访问:Java 堆对象的不居中就必须考虑如何放置访问类型数据的相关信息,而 reference 中存储的直接就是对象地址。

4.4 Субъект умирает

Алгоритм подсчета ссылок: добавьте к объекту счетчик ссылок. Всякий раз, когда на объект ссылаются в одном месте, счетчик увеличивается на 1; всякий раз, когда ссылка на объект недействительна, счетчик уменьшается на 1. Но когда счетчик равен 0, это означает, что на объект нет ссылки.

Алгоритм анализа достижимости: начните с ряда корневых узлов, называемых «GC Roots», и выполните поиск по цепочке ссылок. Все объекты в цепочке ссылок не будут переработаны.

// Корневой объект GC: корневой объект для достижимости

Объекты, на которые ссылается стек виртуальной машины Java, параметры, локальные переменные, временные переменные и т. д. каждого вызова потока. Объекты, на которые ссылаются статические свойства класса в области метода, такие как статические переменные ссылочного типа. Объект, на который ссылается константа в области метода. Объект, указанный в собственном стеке методов. Ссылки внутри виртуальной машины Java, объекты класса, соответствующие базовым типам данных, и некоторые резидентные объекты исключений. Объект, удерживаемый синхронизированной блокировкой.

4.6 Классический загрузчик

Что такое загрузчик классов

ClassLoader используется для загрузки классов Java в виртуальную машину Java.Вообще говоря, виртуальная машина Java использует классы Java следующим образом: Исходные программы Java (файлы .java) преобразуются после компиляции компилятором Java в байт-код Java ( .class), загрузчик классов отвечает за чтение байт-кода Java и преобразование его в экземпляр класса java.lang.Class.

время возникновения

  • 1. При встрече с четырьмя инструкциями байт-кода new, getstatic, putstatic и invokestatic, если класс не был инициализирован, вам нужно сначала запустить его инициализацию.
  • 2. При использовании метода пакета java.lang.reflect для вызова рефлексии к классу, если класс не был инициализирован, его инициализацию необходимо сначала запустить.
  • 3. Когда класс инициализируется, если обнаруживается, что его родительский класс не был инициализирован, необходимо сначала инициировать инициализацию его родительского класса.
  • 4. Когда начинается виртуальная машина, пользователю необходимо указать основной класс для выполнения, то есть вызовите его метод # Main (String [] args), и виртуальная машина первым инициализирует основной класс.
  • 5. При использовании динамического языка JDK7, если окончательный результат синтаксического анализа экземпляра java.lang.invoke.MethodHandle является дескриптором метода REF_GETSTATIC, REF_PUTSTATIC, REF_INVOKESTATIC, и этот дескриптор метода не имеет инициализации, то сначала необходимо запустить его инициализацию.

Как загрузить класс

  • Первый этап, загрузка, заключается в поиске файла .class и загрузке содержащегося в нем байт-кода в память.
  • Второй этап, связывание, можно разделить на три шага, а именно проверку байт-кода, анализ структуры данных класса и соответствующее выделение памяти, а также окончательный анализ таблицы символов.
  • Третий этап, Инициализация (статические свойства и назначения инициализации в классе) и Использование (выполнение статических блоков) и т. д.

4.7 Подробное объяснение ClassLoader

// Java 中有三个类加载器
1. Bootstrap CLassloder 
	最顶层的加载类,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。我们可以打开我的电脑,在上面的目录下查看,看看这些jar包是不是存在于这个目录。 
	
2. Extention ClassLoader 
	扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。 

3. AppClassLoader
	加载当前应用的classpath的所有类


// classLoad 加载流程
Java 基于 Launcher 入口应用
- Launcher初始化了ExtClassLoader和AppClassLoader
-       
      
// 知识点
1 父加载器不是父类
2 Bootstrap ClassLoader是由C/C++编写的        
        
// 常用方法
- 获取父加载器 : cl.getParent() , cl.getParent().getParent()
- 通过指定的全限定类名加载class : loadClass()

    
    

// 双亲委派
1 首先判断这个class是不是已经加载成功
2 当 class 未加载 , 先异常往根节点查找 , 是否上层加载器已经加载 (其中如果某个层已经加载 , 则直接返回)
3 当到 Bootstrap classloader 仍然未加载 , 则由 Bootstrap classloader 到指定的路径查找 , 如果没有查找到 ,则由子加载器继续到其对应路径查找
4 到此时仍然没有查找到 ,则返回异常
    
// 流程 TODO :
   
// 思考 : 
加载对象的时候是从顶层向下 , 查找对象是由底层向上
业务中我们是能够定义多个 Classloader , 使用双亲委派避免不知道在哪个 classLoader 中查找 , 也避免重复加载的问题
    
    
    

4. файл 8 класса

// Class 加载流程 
1. .java 文件编译后 , 生成一个class文件
2. classloader通过相关的规则初次找到这个class
3. 然后会读取class的头文件,包括以下几种数据
	a. 0xCAFEBABE:判断是否为Java编译
	b. 50 , 0:判断版本号
4. String, ArrayList分别有不同层次的loader加载,最顶层的叫Bootstrap Classloader , 下一次级叫Extension Classloader,最底层App Classloade
5. 接着class会被加载到方法区 , 在堆中new 出的该class类的对象来确认class是否被加载
6. 每个class会有局部变量区,还有一个操作数栈 , 线程就会按照流程执行,例如取出局部变量区的数据,放入栈中,最后运行后变成一个数后重新放入
7. 接中从栈中取出结果,重新放入变量区
8. 而线程也不一定只有一个工作台,也可能有多个,但是只在最上面工作(多线程情况),这每个工作台叫栈帧,而多个工作台就是方法调用方法的结果
    
    
// Java 对象头
    GC分代信息,锁信息,哈希码,指向Class类元信息的指针
	Hotspot 虚拟机的对象头主要包括两部分数据:Mark Word(标记字段)、Klass Pointer(类型指针)
	- Klass Point 是是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例
    - Mark Word 用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等。Java 对象头一般占有两个机器码(在 32 位虚拟机中,1 个机器码等于 4 字节,也就是 32 bits)。但是如果对象是数组类型,则需要三个机器码,因为 JVM 虚拟机可以通过 Java 对象的元数据信息确定 Java 对象的大小,无法从数组的元数据来确认数组的大小,所以用一块来记录数组长度。

    
// Java 对象实例数据
    实例数据部分是对象真正存储的有效信息
    
// Java 对象对齐填充    
	虚拟机规范要求对象大小必须是8字节的整数倍
    

Структура хранения заголовка объекта Java 32 битаTODO: нужно улучшить

Хорошая рекомендация статьи@Углубленное понимание структуры байт-кода Java из файла класса — требуется программист

image.png

Способы просмотра байт-кода

// 方法一 : Java 基本工具类
- 查看基本的字节码 : javap java.lang.Object
- 查看基本成员 : javap -p
- 查看详细信息 : javap -v
- 反汇编整个类 : javap -c 

// 方法二 : 使用 ASM 查询
<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm</artifactId>
    <version>8.0.1</version>
</dependency>
<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm-util</artifactId>
    <version>8.0.1</version>
</dependency>

try {
    ClassReader reader = new ClassReader("java.lang.Object");
    StringWriter sw = new StringWriter();
    TraceClassVisitor tcv = new TraceClassVisitor(new PrintWriter(System.out));
    reader.accept(tcv, 0);
} catch (IOException e) {
    e.printStackTrace();
}

// 方法三 : BCEL
<dependency>
    <groupId>org.apache.bcel</groupId>
    <artifactId>bcel</artifactId>
    <version>6.5.0</version>
</dependency>

try { 
    JavaClass objectClazz = Repository.lookupClass("java.lang.Object");
    System.out.println(objectClazz.toString());
} catch (ClassNotFoundException e) { 
    e.printStackTrace(); 
}

// 方法四 : Javassist 
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.27.0-GA</version>
</dependency>

try {
    ClassPool cp = ClassPool.getDefault();
    ClassFile cf = cp.get("java.lang.Object").getClassFile();
    cf.write(new DataOutputStream(new FileOutputStream("Object.class")));
} catch (NotFoundException e) {
    e.printStackTrace();
}

// 方法五 : Jclasslib (IDEA 插件)

4.9 Представление объектов в JVM -- ООП-класс

HotSpot 通过 OOP-Klass 模型来在虚拟机中表示一个对象 , 这里的 OOP 指的是 Ordinary Object Pointer (普通对象指针),它用来表示对象的实例信息,看起来像个指针实际上是藏在指针里的对象。而 Klass 则包含元数据和方法信息,用来描述Java类。

作用 : 
	避免让每个对象中都含有一个vtable(虚函数表),所以就把对象模型拆成klass和oop,其中oop中不含有任何虚函数,而Klass就含有虚函数表,可以进行method dispatch。

Klass : Java类在HotSpot中的c++对等体,用来描述Java类 , 在加载过程中创建
	- 实现语言层面的Java类
	- 实现Java对象的分发功能
	
OOP : 在Java程序运行过程中new对象时创建的 , 包含以下部分
    - instanceOopDesc,也叫对象头
        - Mark Word,主要存储对象运行时记录信息,如hashcode, GC分代年龄,锁状态标志,线程ID,时间戳等
        - 元数据指针,即指向方法区的instanceKlass实例
    - 实例数据

5. ГХ-мониторинг

5.1 Когда ГХ мониторит

GC 监控是指监控 JVM 执行 GC	的过程
例如 :
	> 何时一个新生代被移动到老年代,以及其中被花费的时间
	> stop the world 何时发生,执行了多长时间
	
> GC 访问的接口 : GUI / CUI 两大类
	: cUI GC 监控方法使用的独立的 jstat 的 CUI 应用
	: cUI 或者在启动的时候选择JVM 参数 verbosegc
	: GUI GC 由一个单独的图形化界面完成 : jconsole ,jvisualvm , Visual GC
	

jstat :
参数名称见附录 

-verbosegc : 启动 Java 应用时可指定

5.2 Общие инструменты мониторинга ГХ

• jps :虚拟机进程状况工具
	JVM Process Status Tool ,显示指定系统内所有的HotSpot虚拟机进程。
    -q:忽略输出的类名、Jar名以及传递给main方法的参数,只输出pid。
    -m:输出传递给main方法的参数,如果是内嵌的JVM则输出为null。
    -l:输出完全的包名,应用主类名,jar的完全路径名
    -v:输出传给jvm的参数
    -V:输出通过标记的文件传递给JVM的参数(.hotspotrc文件,或者是通过参数-XX:Flags=指定的文件)。
    -J 用于传递jvm选项到由javac调用的java加载器中,
        
• jstat :虚拟机统计信息监控工具
	JVM statistics Monitoring ,是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。 常见的用法包括类的加载及卸载情况 , 查看新生代、老生代及持久代的容量及使用情况 , 查看新生代、老生代及持久代的垃圾收集情况,包括垃圾回收的次数及垃圾回收所占用的时间 , 查看新生代中Eden区及Survior区中容量及分配情况
        
• jinfo :Java 配置信息工具
	JVM Configuration info ,这个命令作用是实时查看和调整虚拟机运行参数。
        
• jmap :Java 内存映射工具
	JVM Memory Map ,命令用于生成 heap dump 文件。
        
• jhat :虚拟机堆转储快照分析工具
	JVM Heap Analysis Tool ,命令是与 jmap 搭配使用,用来分析 jmap 生成的 dump 文件。jhat 内置了一个微型 的HTTP/HTML 服务器,生成 dump 的分析结果后,可以在浏览器中查看。
        
• jstack :Java 堆栈跟踪工具
	Java Stack Trace ,用于生成 Java 虚拟机当前时刻的线程快照。
        
• HSDIS :JIT 生成代码反编译

// Java 自带
• JConsole :Java 监视与管理控制台
	Java Monitoring and Management Console 是从 Java5 开始,在 JDK 中自带的 Java 监控和管理控制台,用于对 JVM 中内存,线程和类等的监控。
• VisualVM :多合一故障处理工具
	JDK 自带全能工具,可以分析内存快照、线程快照、监控内存变化、GC变化等。
	特别是 BTrace 插件,动态跟踪分析工具。

// 其他
• MAT :内存分析工具
• [GChisto](GC 日志分析工具 —— GChisto) :一款专业分析 GC 日志的工具。
    
    
// JMC : Java Mission Control 
-> 完整的图形化界面
-> 提供对象查看    

5.3 Мониторинг общих команд

// 获取 Java 程序使用的内存
Runtime#freeMemory() 方法,返回剩余空间的字节数。
Runtime#totalMemory() 方法,总内存的字节数。
Runtime#maxMemory() 方法,返回最大内存的字节数。

5.4 Метод анализа ГХ

// --------------- jconsole 使用
	- 控制台直接输入 : jconsole
	- 1 选择需要调试的本地连接 , 点击连接
	- 2 选择远程连接 , 输入用户名 , 口令连接
        
// -------------- jvisualvm 使用
	- 找到 JDK 的安装目录 , 点击运行 jvisualvm.exe
	- 右侧直接选择运行中的应用     

// --------------- jstat 使用 (命令行)
jstat <option> [-t] [-h] <pid>  <interval> <count>
  参数解释:
    option   可以从下面参数中选择
        -class                 显示ClassLoad的相关信息;
        -compiler           显示JIT编译的相关信息;
        -gc                     显示和gc相关的堆信息;
        -gccapacity     显示各个代的容量以及使用情况;
        -gccause             显示垃圾回收的相关信息(通-gcutil),同时显示最后一次或当前正在发生的垃圾回收的诱因;
        -gcnew               显示新生代信息;
        -gcnewcapacity  显示新生代大小和使用情况;
        -gcold                 显示老年代和永久代的信息;
        -gcoldcapacity    显示老年代的大小;
        -gcpermcapacity 显示永久代的大小;
        -gcutil             显示垃圾收集信息;   
        -printcompilation输出JIT编译的方法信息;
    -t         可以在打印的列加上Timestamp列,用于显示系统运行的时间
    -h     可以在周期性数据数据的时候,可以在指定输出多少行以后输出一次表头
    interval 执行每次的间隔时间,单位为毫秒
    count   用于指定输出多少次记录,缺省则会一直打印  
	
案例 : 
|- : Jstat -cpmpiler pid
|- 查看pid为23814的ClassLoad相关信息,每秒钟打印一次,总共打印5次 : jstat -gc pid 1000 5      
|- 显示各个代的容量的信息 :  jstat -gccapacity pid
|- 显示最近一次GC的原因 :  jstat -gccause pid
|- 显示新生代的详细信息 : jstat -gcnew pid:
|- 输出新生代各个区的详细信息 : jstat -gcnewcapacity pid
|- 显示老年代GC的详细情况 : jstat -gcold pid
|- 输出老年代的详细信息 : jstat -gcoldcapacitp pid
|- 查看每个代区域使用的百分比情况 : jstat -gcutil pid

// ------------------- jmap 使用
jmap [option] vmid
    
-dump : 生成Java堆转储快照
-heap:显示Java堆详细信息
-histo:显示堆中对象统计信息
    
案例 :
|- 使用jmap 生成快照文件 : jmap -dump:format=b,file=jsconsole.bin 7020
|- 生成一个正常运行的jconsole的快照的实例 : jps
|- 查看堆栈信息 : jmap -heap pid
|- 使用jmap 生成快照文件 : jmap -dump:format=b,file=jsconsole.bin 7020    
    
// --------------------- jhat 使用
Step 1 : 导出栈
	jmap -dump:live,file=a.log pid
Step 2 : 分析堆文件
	jhat -J-Xmx512M a1.log
Step 3 : 查看
	http://ip:7000/
Step 4 : 使用 SQL 查询
    select <javascript expression to select>
    [from [instanceof] <class name> <identifier>]
    [where <javascript boolean expression to filter>]
    
(1)class name是java类的完全限定名,如:java.lang.String, java.util.ArrayList, [C是char数组, [Ljava.io.File是java.io.File[]
(2)类的完全限定名不足以唯一的辨识一个类,因为不同的ClassLoader载入的相同的类,它们在jvm中是不同类型的
(3)instanceof表示也查询某一个类的子类,如果不明确instanceof,则只精确查询class name指定的类
(4)from和where子句都是可选的
(5)java域表示:obj.field_name;java数组表示:array[index]
                                                                               
案例 :                                                              
(1)查询长度大于100的字符串
	select s from java.lang.String s where s.count > 100
(2)查询长度大于256的数组
	select a from [I a where a.length > 256
(3)显示匹配某一正则表达式的字符串
	select a.value.toString() from java.lang.String s where /java/(s.value.toString())
(4)显示所有文件对象的文件路径
	select file.path.value.toString() from java.io.File file
(5)显示所有ClassLoader的类名
	select classof(cl).name from instanceof java.lang.ClassLoader cl
(6)通过引用查询对象
	select o from instanceof 0xd404d404 o
                   
https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html 
                   
// ------------- jvm jinfo
|- 查看 JVM 参数 : jinfo -flags process_id
|- 查看java系统参数 : jinfo -sysprops process_id                   

5.5 Расширение инструментов стресс-тестирования

// 常用的压测工具
1 LoadRunner  : 预测系统行为和性能的负载测试工具
2 Apache JMeter : 开源压测产品
3 NeoLoad : 负载和性能测试工具
4 WebLOAD : 来自Radview公司的负载测试工具,它可被用以测试系统性能和弹性,也可被用于正确性验证
5 阿里云PTS : 一个SaaS性能测试平台,具有强大的分布式压测能力
6 Loadstorm : 一款针对Web应用的云端负载测试工具,通过模拟海量点击来测试Web应用在大负载下的性能表现
7 CloudTest : 一个集性能和功能测试于一体的综合压力测试云平台
8 Load impact : 一款服务于DevOps的性能测试工具,支持各种平台的网站、Web应用、移动应用和API测试
    
// JMeter 使用
    

5.6 Использование Jstack

> Step 1 : 拿到 pid
ps -ef | grep java

> Step 2 : 查看资源进程
top -Hp 30275

printf "%x\n" 3440


> 简单使用
jstack 30275
    
> 查看指定进程
printf "%x\n" 17880  
jstack 17880|grep 45d8 -A 30
    
// 查看 TimeWait 


// Windows 版本
netstat -ano |findstr "80" windows
netstat -an | find "TIME_WAIT" /C    
    
// jstack 统计线程数
jstack -l 28367 | grep 'java.lang.Thread.State' | wc -l    
    
    
// jstack 

Usage:
    jstack [-l] <pid>
        (to connect to running process) 连接活动线程
    jstack -F [-m] [-l] <pid>
        (to connect to a hung process) 连接阻塞线程
    jstack [-m] [-l] <executable> <core>
        (to connect to a core file) 连接dump的文件
    jstack [-m] [-l] [server_id@]<remote server IP or hostname>
        (to connect to a remote debug server) 连接远程服务器

Options:
    -F  to force a thread dump. Use when jstack <pid> does not respond (process is hung)
    -m  to print both java and native frames (mixed mode)
    -l  long listing. Prints additional information about locks
    -h or -help to print this help message
        
        
        
    

5.7 Использование JOL

JOL:查看Java 对象布局、大小工具

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>put-the-version-here</version>
</dependency>

static Object generate() {
	Map<String, Object> map = new HashMap<>();
	map.put("a", new Integer(1));
	map.put("b", "b");
	map.put("c", new Date());
 
	for (int i = 0; i < 10; i++) {
		map.put(String.valueOf(i), String.valueOf(i));
	}
	return map;
}

查看对象内部信息: ClassLayout.parseInstance(obj).toPrintable()
查看对象外部信息:包括引用的对象:GraphLayout.parseInstance(obj).toPrintable()
查看对象占用空间总大小:GraphLayout.parseInstance(obj).totalSize()

5.8 Thread Dump

Thread Dump是非常有用的诊断Java应用问题的工具。每一个Java虚拟机都有及时生成所有线程在某一点状态的thread-dump的能力,虽然各个 Java虚拟机打印的thread dump略有不同,但是大多都提供了当前活动线程的快照,及JVM中所有Java线程的堆栈跟踪信息,堆栈信息一般包含完整的类名及所执行的方法,如果可能的话还有源代码的行数。

1. 查找内存泄露,常见的是程序里load大量的数据到缓存;
2. 发现死锁线程;


// Linux 抓取 Dump 的方式 (20810 是 jstack 在Java 目录下 )
jstack -l 20810 | tee -a /opt/jstack.log

// 简单学习 : 
// 虚拟机信息
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.251-b08 mixed mode):

// 线程info信息块:
// 线程名称 - #36 - 线程类型 (daemon) - 优先级 (prio)
// tid : JVM 线程ID
// nid : 对应系统线程id
// 线程状态:in Object.wait().
// 起始栈地址:[0xae77d000]
"Attach Listener" #36 daemon prio=9 os_prio=0 tid=0x00007f5fec001000 nid=0x6658 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
	- None
        
// 堆栈信息
线程状态 - java.lang.Thread.State: WAITING (parking)
线程抛出节点 -	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000000e4e7bdf8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
	at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:107)
	at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:33)
	at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

// 方案
cpu飙高,load高,响应很慢 --> 单请求 dump 多次
查找占用cpu最多的线程信息 --> 对对应的线程进行 dump , 先 top 查询对应 id
cpu使用率不高但是响应很慢 --> 进行dump,查看是否有很多thread struck在了i/o、数据库等地方,定位瓶颈原因
请求无法响应 --> 多次dump,对比是否所有的runnable线程都一直在执行相同的方法

// 常见分析        
死锁 , 热锁  

5.9 Детали журнала GC

// 使用方法 : 
-XX:+PrintGCDetails

// 参数含义 :  @ https://www.cnblogs.com/xuwenjin/p/13092857.html
GC:表示进行了一次Minor GC,即从年轻代空间(包括 Eden 和 Survivor 区域)回收内存

Allocation Failure:在年轻代中没有足够的空间能够存储新的数据

Full GC (Ergonomics):表示进行了一次Full GC,即清理整个堆空间(包含年轻代和老年代)

PSYoungGen: 1024K->1024K(1536K):垃圾回收器是Paralle Scavenge,年轻代区GC前->GC后该区域已使用量,后面的1536表示该区域总量

ParOldGen: 4032K->4032K(4096K):老年代区,GC前->GC后该区域已使用量,后面的4096表示该区域总量

5056K->5056K(5632K):GC前 -> GC后Java堆的使用量,后面的5632表示Java堆总量

Metaspace: 3117K->3117K(1056768K):JDK8中取消了永久代,新增了一个叫元空间(Metaspace)的区域,对应的还是JVM规范中的方法区(主要存放一些class和元数据的信息),该数据表示该区GC前后使用量

0.0214352 secs:暂停STW 时间,即GC的时间

Times: user=0.02 sys=0.01, real=0.02 secs:更为详细的时间占比统计


6. Оптимизация сборщика мусора

6.1 Предпосылка оптимизации сборщика мусора

> GC 优化永远是最后一项任务

> 原则 :
	> 将转移到老年代的对象数量降到最少
		:调整新生代空间的大小。
	> 减少 Full GC 的执行时间
		: 你需要将老年代空间设定为一个“合适”的值

6.2 Схема оптимизации сборщика мусора

> 使用  StringBuilder 或者StringBuffer 来替代String
> 尽量少的输出日志

GC 优化考虑的参数

6.3 Параметры, которые следует учитывать при оптимизации ГХ

image.png

6.4 Дополнительные параметры типа GC

image.png

6.5 Процесс оптимизации GC

1 > 监控 GC 状态
2 > 分析监控结果 , 考虑是否需要GC
3 > 调整 GC 类型 , 分配存储空间
4 > 分析结果

6.6 Ситуация и анализ переполнения памяти

> 1 堆栈溢出
	- java.lang.OutOfMemoryError: ......java heap space.....
	- 看到heap相关的时候就肯定是堆栈溢出 , 适当调整 -Xmx和-Xms
	- 访问量太多并且每个访问的时间太长或者数据太多,导致数据释放不掉
        - java.lang.OutOfMemoryError:GC over head limit exceeded -- 系统处于高频的GC状态,而且回收的效果依然不佳
> 2 PermGen的溢出
	- java.lang.OutOfMemoryError: PermGen space
	- 系统的代码非常多或引用的第三方包非常多、或代码中使用了大量的常量、或通过intern注入常量、或者通过动态代码加载等方法,导致常量池的膨胀
	-XX:PermSize和-XX:MaxPermSize的大小
        
> 3 ByteBuffer中的allocateDirect() 溢出
    - java.lang.OutOfMemoryError: Direct buffer memory
    -直接或间接使用了ByteBuffer中的allocateDirect方法的时候,而不做clear的时候就会出现类似的问题
	-XX:MaxDirectMemorySize
        
> 4 java.lang.StackOverflowError
    - java.lang.StackOverflowError
    - -Xss太小了,我们申请很多局部调用的栈针等内容是存放在用户当前所持有的线程中的
        
> 5 java.lang.OutOfMemoryError: unable to create new native thread
    - 说明除了heap以外的区域,无法为线程分配一块内存区域了,这个要么是内存本身就不够,要么heap的空间设置得太大了

> 6 java.lang.OutOfMemoryError: request {} byte for {}out of swap
    - 一般是由于地址空间不够而导致

6.7 Полный анализ причин и решение GC

// 原因 : 
1 . 应用程序创建了太多无法快速回收的对象。
2 . 当堆被分割时,即使有很多空闲空间,在老代中直接分配也可能失败

https://blog.gceasy.io/2020/06/02/simple-effective-g1-gc-tuning-tips/
https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html

Семь маленьких точек знаний

7.1 Баночка, связанная с

TODO

7 . 2 CPU

TODO

Восемь других

# Какова максимальная память кучи для 32-битной JVM и 64-битной JVM

理论上说上 32 位的 JVM 堆内存可以到达 2^32,即 4GB
4 位 JVM 允许指定最大的堆内存,理论上可以达到 2^64 

# Прямая память (память вне кучи)

> 直接内存(Direct Memory),并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中农定义的内存区域
> NIO(New Input/Output) 类,引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用 native 函数库直接分配堆外内存,然后通脱一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作

// 对比
直接内存申请空间耗费更高的性能,当频繁申请到一定量时尤为明显
直接内存 IO 读写的性能要优于普通的堆内存,在多次读写操作的情况下差异明显

# Другие инструменты

> GCEasy : 

# Процесс анализа JMC

// Step 1 : 开启指定应用的飞行记录

// Step 2 : 分析模块 , 飞行记录提供了以下几个模块
> 一般信息 :  | 概述  | JVM 信息 | 系统属性 | 记录
    - CPU 占用率 : 可以判断是否CPU占满导致的缓慢
    - 堆使用率 : 内存使用情况会导致垃圾收集的频率 , Redis 的使用(AOF/RDB持久化异常) ,
    - JVM 信息 : 可以了解到当前使用的虚拟机类型(不同类型虚拟机会使用不同的回收策略 , 以及使用的JDK , 配置的 JVM 参数等)
        
> 内存 : | 概述 | 垃圾收集 | GC 时间 | GC 配置 | 分配 | 对象统计信息
    - 概述 : 
		- GC 配置 : 包含 GC 的配置信息 , 以及对应的收集器类型
		- GC 统计时间 :  
    - 垃圾收集 : 包含垃圾收集的次数和消耗的时间
        -> 垃圾收集的频率是否正常 , 是否过于频繁
        -> 每次消耗的时候会不会太长 ?stop-world 后会影响其他的运行
	- GC 时间 : 该时间为不同年龄代的时间
	- GC 配置 : 配置的 GC 线程数 , 堆内存等配置详情
	- 分配 : 主要是 TLAB 的分配情况 , 

> 代码 : | 概述 | 热点方法 | 调用树 | 异常错误 | 编译 | 类加载
    - 热点方法 : 判断代码中对相关方法的调用是否合理(对应类的堆会不会过大 , 对象会不会过多)
    - 热点方法 : 判断常用的对象会不会有多线程风险及死锁风险 , 是否效率过低
    - 调用树 : 通过调用树追溯问题的根源
    - 异常错误 : 判断是否存在异常
    
> 线程 : | 概述 | 热点线程 | 争用 | 等待时间 | 线程转储 | 锁定实例
    - 判断死锁
    - 判断线程的销毁情况
    - 判断是否有切换线程带来的损失 (频繁切换) , 热锁的情况
    - 判断线程是否合理使用了线程池等工具
    
> IO :   | 概述 | 文件读写 | 套接字读写 | 
    - 这个模块可以有效的分析是否为文件读写时间导致的延迟或者套接字访问导致的系统缓慢
    
> 系统信息及环境变量 , 略
    
> 事件 : 发生的事件比例 , 包括 Java Thread Park , Java Thread start , Java Thread end 等    

# процесс анализа неба

skywalking 是链路分析工具 , 是很好的辅助工具 , 能快速的分析瓶颈点

// 使用方式(不过多简述 , 很多) , 注意需要点击一下刷新才会出数据
-javaagent:D:\java\plugins\shywalking\agent82\skywalking-agent.jar    
    
// 常用用法: 
- 仪表盘 : 用于查看各服务器状态    
- 拓扑图 : 用于分析服务器的结构是否合理 (redis 服务器 , mysql 服务器 , 等等其他的)
- 追踪 : 可以快速判断慢SQL , 慢 接口    
- 性能分析 : (创建的端点是追踪里面的端点,点击分析后可以直接追踪到对应的代码行)    

Девять оптимизированных процессов

9.1 Процесс измерения давления

> 对连接数进行优化 , 包括
	- Mysql 连接数 
	- redis 连接数
	- Mysql 连接池数量 (连接池数量不是越大月好)
	- Redis 连接池数量
	- tomcat 连接数
	- TCP 连接数 (包括指定端口 , 随机端口 , 端口总数限制)
	- LDAP 连接数
	- OpenFile 连接数
	
> 对 GC 进行分析
	- 1 查看 CPU 使用情况 : top
	- 2 查看指定进程的使用 : top -Hp [进程ID]
	- 3 分析当前进程栈 : jstack [进程ID] > jstack_01
	- 4 查看 GC 情况 : jstat -gcutil [进程id] 1000
	- 5 查看堆内存 : jmap -histo [进程id] > jmap.txt
	- 6 打印堆内存快照 : jmap -dump:format=b,file=aa.bin 1232134
	
> 对 GC 进行优化
	// 80 时进行 GC
	- -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly
	// 保留 GC log 
	- -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log
	
>  负载均衡
	- 判断负载的方式是轮询还是压力 
	
// 优化 新生代容积
-Xmn350M -> -Xmn800M
-XX:SurvivorRatio=4 -> -XX:SurvivorRatio=8
-Xms1000m ->-Xms1800m
    
// 优化 metaspace
-Xmn350M -> -Xmn800M
-Xms1000M ->1800M
-XX:MetaspaceSize=200M
-XX:CMSInitiatingOccupancyFraction=75    	

Приложение:

Общие аргументы JVM

Имя параметра Jstat

описывать имя параметра
Выводит текущее свободное и используемое пространство для каждой области кучи (Eden, Survivor и т. д.), общее количество выполнений GC и совокупное время, затраченное на операции GC. gc
Выводит минимальное ограничение пространства (мс)/максимальное ограничение пространства (мкс) для каждой области кучи, текущий размер, количество раз, когда GC выполнялся поверх каждой области. (Не выводит текущее используемое пространство и время выполнения GC). gccapactiy
Выходные данные — информация, предоставленная gcutil, а также причина последнего выполнения GC и причина текущего выполняемого GC. gccause
Вывод данных о производительности GC пространства нового поколения gcnew
Вывести статистику по размеру пространства нового поколения. gcnewcapacity
Вывод данных о производительности сборщика мусора для пространства старого поколения. gcold
Вывести статистику по размеру пространства старой генерации. gcoldcapacity
Вывести статистику о размере постоянного пространства. gcpermcapacity
Выведите процент использования каждой области кучи, а также общее количество выполнений GC и событий, затраченных операциями GC. gcutil
Список инструкция параметры Jstat
S0C Выведите размер пространства Survivor0. Блок КБ. -gc -gccapacity -gcnew -gcnewcapacity
S1C Выведите размер клетки Survivor1. Блок КБ. -gc -gccapacity -gcnew -gcnewcapacity
S0U Выведите размер используемого пространства Survivor0. Блок КБ. -gc -gcnew
S1U Выведите размер используемого пространства Survivor1. Блок КБ. -gc -gcnew
EC Выведите размер пространства Эдема. Блок КБ. -gc -gccapacity -gcnew -gcnewcapacity
EU Выведите размер используемого пространства Эдема. Блок КБ. -gc -gcnew
OC Выведите размер пространства старого поколения. Блок КБ. -gc -gccapacity -gcold -gcoldcapacity
OU Выводит размер пространственного пространства для старшего. Блок КБ. -gc -gcold
PC Размер выходного постоянного пространства генерации. Блок КБ. -gc -gccapacity -gcold -gcoldcapacity -gcpermcapacity
PU Выведите размер используемого пространства для постоянного поколения. Блок КБ. -gc -gcold
YGC Количество раз, когда время GC пространства нового поколения встречается. -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause
YGCT Время, потраченное на обработку GC молодого поколения. -gc -gcnew -gcutil -gccause
FGC Сколько раз выполнялся полный сборщик мусора. -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause
FGCT Время, проведенное в полных операциях GC -gc -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause
GCT Общее время, проведенное по операциям GC. -gc -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause
NGCMN Минимальная пространственная емкость нового поколения, блок КБ. -gccapacity -gcnewcapacity
NGCMX Максимальная емкость нового поколения, в КБ. -gccapacity -gcnewcapacity
NGC Текущая емкость нового поколения, в КБ. -gccapacity -gcnewcapacity
OGCMN Минимальная емкость старости, в КБ. -gccapacity -gcoldcapacity
OGCMX Максимальный объем памяти в старом возрасте, в КБ. -gccapacity -gcoldcapacity
OGC Текущая космическая емкость системы старости, в килобайтах. -gccapacity -gcoldcapacity
PGCMN Минимальная емкость постоянного поколения в КБ. -gccapacity -gcpermcapacity
PGCMX Максимальная емкость постоянного поколения в КБ. -gccapacity -gcpermcapacity
PGC Текущая емкость постоянного поколения в КБ. -gccapacity -gcpermcapacity
PC Текущий размер постоянного поколения, в КБ -gccapacity -gcpermcapacity
PU Размер используемого в настоящее время пространства постоянного поколения, в КБ -gc -gcold
LGCC Причина, по которой произошел последний GC -gccause
GCC Причина, по которой происходит текущий GC -gccause
TT Порог старения. Количество пустых пережитков в молодом поколении перед переходом в старое поколение. -gcnew
MTT Максимальный порог старения. Количество пустых пережитков в молодом поколении перед переходом в старое поколение. -gcnew
DSS Размер пространства, необходимого для области выживания, в КБ. -gcnew

Быстрая проверка общих конфигураций виртуальных машин

инструкция Заказ
Включить журнал GC (java8) -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:{file-path}
Включить журнал GC (Java9) -Xlog:gc*:file={file-path}

Спасибо

// 此篇笔记是一个总结分析的笔记 , 时间周期较长 , 很多知识点已经难以追溯出处 , 如果此处遗漏了某位道友 ,敬请谅解

Java 技术驿站 , 一系列死磕看的相当爽
http://cmsblogs.com/?p=5140

CSDN
http://blog.csdn.net/linxdcn/article/details/72896616

芋道源码 , 很不错的源码博客
http://www.iocoder.cn/

掘金老哥
https://juejin.im/post/5c31dca7e51d45524975d046

CSDN 
https://blog.csdn.net/briblue

以及所有对该文章有所帮助的表示感谢

Список изменений

V20210817 : изменить общий стиль. V20210818 : Дополнительная документация