JVM之运行时数据区、内存结构、内存模型

运行时数据区

JVM运行时数据区、JVM内存结构、JVM内存模型都是指Java虚拟机的内存空间划分,主要分为5个部分:程序计数器、Java虚拟机栈、本地方法栈、堆、方法区

注意:JVM内存模型与Java内存模型有严格不同,Java内存模型是与多线程编程相关。

在这里插入图片描述

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,统称运行时数据区域。

在类加载过程中的装载阶段:将Calss文件对应字节流所代表的静态存储结构转化为方法区的运行时数据结构,在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。

也就是说:类文件被类装载器装载进来之后,类中的内容,如变量,常量,方法,对象等数据得需要存储在JVM中对应的空间中,该空间就是运行时数据区。

JDK8同JDK7比,最大差别是:元数据区取代了永久代。元空间Metaspace本质和永久代Perm Space类似,都是对JVM 规范中方法区的实现。不过元空间与永久代之间最大区别在于:元数据空间并不在虚拟机中,而是使用本地内存。

运行时数据区分为2类:

线程私有的:

程序计数器

虚拟机栈

本地方法栈

线程共享的:

堆

方法区

直接内存 (非运行时数据区的一部分)

常量池

常量池分为静态常量池,运行时常量池,还有字符串常量池。

静态常量池

储存的就是字面量以及符号引用

字符串常量池

字符串常量池是JVM为了提升性能和减少内存消耗针对字符串(String类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。

JDK7之前,字符串常量池存放在永久代。JDK7字符串常量池和静态变量从永久代移动到Java堆中。

这是因为永久代的GC回收效率太低,只有在Full GC的时候才会被执行回收。Java程序中通常会有大量被创建的字符串等待回收,将字符串常量池放到堆中,能够更高效及时地回收字符串内存。

在堆中创建字符串对象A,同时会将字符串对象A的引用保存在字符串常量池中

String a = "A";

再次创建一个字符串A时,会直接返回字符串常量池中字符串对象A的引用

String b= "A";

System.out.println(a == b)

运行时常量池

Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有用于存放编译期生成的各种字面量和符号引用的常量池表

字面量是源代码中的固定值的表示法,通过字面就能知道其值的含义。字面量包括整数、浮点数和字符串字面量

符号引用包括类符号引用、字段符号引用、方法符号引用和接口方法符号引用

常量池表会在类加载后存放到方法区的运行时常量池中

运行时常量池的功能类似编程语言的符号表,也是方法区的一部分,常量池无法再申请到内存时会抛出OutOfMemoryError错误。

方法区(Method Area)

方法区属于是JVM运行时数据区域的一块逻辑区域,是各个线程共享的内存区域,在虚拟机启动时创建

Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却又一个别名叫做Non-Heap(非堆),目的是与Java堆区分开来

当虚拟机要使用一个类时,它需要读取并解析Class文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

永久代与元空间

永久代以及元空间是HotSpot虚拟机对虚拟机规范中方法区的两种实现方式。并且,永久代是JDK8之前的方法区实现,JDK8及以后方法区的实现变成了元空间。最大的区别在于:元空间不在虚拟机设置的内存中,而是使用本地内存

永久代有一个JVM本身设置的固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。

元空间里面存放的是类的元数据,加载多少类的元数据就不由MaxPermSize控制, 而由系统的实际可用空间来控制,这样能加载的类就更多。

方法区参数设置

JDK8之前

-XX:PermSize:设置永久代初始分配空间,默认值是20.75M

-XX:MaxPermSize:设定永久代最大可分配空间,32位机器默认是64M,64位机器默认是82M

当永久代溢出时会得到错误:java.lang.OutOfMemoryError: PermGen space

JDK8

-XXMaxMetaspaceSize:设置最大元空间大小,默认值为unlimited,只受系统内存的限制

-XXMetaspaceSize:定义元空间的初始大小,如果未指定,则Metaspace将根据运行时的应用程序需求动态地重新调整大小

当元空间溢出时会得到错误: java.lang.OutOfMemoryError: MetaSpace

堆(Heap)

Java堆是Java虚拟机所管理内存中最大的一块,在虚拟机启动时创建,被所有线程共享

堆内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存

JDK7与JDK8

JDK7的堆内存模型

JDK7的堆内存由:Young、Tenured、Perm、Virtual等区构成

Young年轻区(代)

Young区被划分为三部分: Eden区、两个大小严格相同的Survivor区(S0+S1),S0和S1一样大,也叫From和To

S0和S1某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制对象用,在Eden区间变满的时候, GC就会将存活的对象移到空闲的Survivor区间中,根据JVM的策略,在经过几次垃圾收集后,任然存活于Survivor的对象将被移动到Tenured区间。

Tenured/Old年老区

Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在Young复制转移一定的次数以后,对象就会被转移到Tenured区,一般如果系统中用了application级别的缓存,缓存中的对象往往会被转移到这一区间。

Perm永久区

Perm代主要保存class、method、filed对象,这部份空间一般不会溢出,除非一次性加载很多的类

Virtual区

最大内存和初始内存的差值,就是Virtual区。

JDK8的堆内存模型

JDK8的内存模型组成:

新生代内存(Young Generation): Young区

老生代(Old Generation): Old区

永久代(Permanent Generation)

Young区分为:Survivor区(S0+S1)、EdenS0S1一样大,也叫FromTo

在这里插入图片描述
JDK7与JDK8的区别:

最大的Perm区,用Metaspace元数据空间进行替换。Metaspace所占用的内存空间不是在虚拟机内部,而是在本地内存空间。

移除永久代是为融合HotSpot JVM与 JRockit VM而做出的努力,因为JRockit没有永久代,不需要配置永久代。

由于永久代内存经常不够用或发生内存泄露,爆出异常java.lang.OutOfMemoryError: PermGen,基于此,将永久区废弃,而改用元空间,改为了使用本地内存空间。

Survivor区

Survivor区是为了:减少老年代对象的产生,进而减少Full GC的发生,Survivor的预筛选保证,只有经历15次Minor GC(回收新生代)还能在新生代中存活的对象,才会被移动到老年代。

如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会移动到老年代

当老年代满了后,触发Major GC(回收老年代),注意:Major GC一般伴随着Minor GC,也可以看做触发了Full GC

老年代内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多,会影响大型程序的执行和响应速度。

假如增加老年代空间,更多存活对象才能填满老年代。虽然降低Full GC频率,但是随着老年代空间加大,一旦发生Full GC,执行所需要的时间更长

假如减少老年代空间,虽然GC所需时间减少,但是老年代很快被存活对象填满,Full GC频率增加。

S0与S1区的出现是为了解决碎片化

如果只有一个Survivor区,新建对象在Eden中,一旦Eden满了,触发Minor GC,Eden中存活对象移动到Survivor区。如此循环,当下一次Eden区进行Minor GC时,Eden和Survivor各有一些存活对象,此时把Eden区的存活对象放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化的产生。

新生代中Eden:S1:S2的比例默认是8:1:1

新生代中的可用内存:复制算法用来担保的内存为9:1

可用内存中EdenS1区为8:1

即新生代中Eden:S1:S2 = 8:1:1

堆内存中都是线程共享的区域吗?

JVM默认为每个线程在Eden上开辟一个buffer区域,用来加速对象的分配,称之为TLAB,全称:Thread Local Allocation Buffer。

对象优先会在TLAB上分配,但是TLAB空间通常会比较小,如果对象比较大,那么还是在共享区域分配。

逃逸分析

随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么绝对。从JDK7开始已经默认开启逃逸分析,如果某些方法中的对象引用没有被返回或者未被外面使用(未逃逸出去),那么对象可以直接在栈上分配内存。

对象的创建

新创建的对象都会被分配到Eden区,一些特殊的大对象会直接分配到Old区。当Eden区空间不足GC后进入Survivor区的From区,或者进入Survivor区的To区,来回切换。通常直到GC回收18次后进入Old区。
在这里插入图片描述

大部分情况,对象都会首先在Eden区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入S0或者S1,并且对象的年龄还会加1(Eden区->Survivor 区后对象的初始年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold设置。

堆内存溢出

当创建大量对象时,堆空间不足,则会堆内存溢出

设置堆大小

-Xmx20M -Xms20M
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);
        List<Object> list = new ArrayList<>();
        while (true) {
            list.add(new Object());
        }
    }
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3210)
	at java.util.Arrays.copyOf(Arrays.java:3181)
	at java.util.ArrayList.grow(ArrayList.java:267)
	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:241)
	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:233)
	at java.util.ArrayList.add(ArrayList.java:464)
	at com.example.demo.DemoApplicationTests.main(DemoApplicationTests.java:44)

方法区内存溢出

向方法区中大量添加Class的信息,当方法区内存不足,则方法区内存溢出

设置Metaspace的大小

-XX:MetaspaceSize=30M -XX:MaxMetaspaceSize=30M

可以借助ASM模拟方法区内存溢出。ASM是一个 Java字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。

虚拟机栈溢出

Stack Space用来做方法的递归调用时压入栈帧(Stack Frame),当递归调用深度太深,就可能耗尽Stack Space,爆出StackOverflow的错误。

JDK5以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。

Stack Space是根据应用线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,在3000~5000左右较为合理。

线程栈设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。

设置每个线程的堆栈大小

-Xss128k
    public static long count = 0;

    public static void method() {
        System.out.println(count++);
        method();
    }

    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(5000);
        method();
    }
1067
1068
1069
1070
1071
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
Exception in thread "main" java.lang.StackOverflowError
	at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)
	at sun.nio.cs.UTF_8.access$200(UTF_8.java:57)
	at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:636)
	at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)
	at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)
	at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)
	at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
	at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
	at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
	at java.io.PrintStream.write(PrintStream.java:526)
	at java.io.PrintStream.print(PrintStream.java:611)
	at java.io.PrintStream.println(PrintStream.java:750)
	at com.example.demo.DemoApplicationTests.method(DemoApplicationTests.java:35)

虚拟机栈(Java Virtual Machine Stacks)

虚拟机栈是在JVM运行过程中存储当前线程运行方法所需的数据,指令、返回地址等信息。使用先进后出(FILO)的数据结构

虚拟机栈是基于线程的,是以线程方式运行,同时也是一个线程执行的区域,保存着一个线程中方法的调用状态。

一个Java线程的运行状态,由一个虚拟机栈来保存,所以虚拟机栈是线程私有的,独有的,随着线程的创建而创建。在线程的生命周期中,参与计算的数据会频繁地入栈和出栈,栈的生命周期是和线程一样的。

每一个被线程执行的方法,为该栈中的栈帧,即每个方法对应一个栈帧。调用一个方法,就会向栈中压入一个栈帧,一个方法调用完成,就会把该栈帧从栈中弹出。

大小限制:缺省为1M,可用参数 –Xss 调整大小,如-Xss256k

官方文档(JDK1.8):https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

压栈入栈与出栈过程

static  void a(){
	b();
}

static  void b(){
	c();
}

static  void c(){

}

 public static void main(String[] args) {
         a();
    }

如上a、b、c3个方法,a栈帧入栈,接着b栈帧入栈,接着c栈帧入栈;然后c栈帧出栈,b栈帧出栈,直到a栈帧出栈

在这里插入图片描述

栈帧

在每个Java方法被调用的时候,都会创建一个栈帧,并入栈。一旦方法完成相应的调用,则出栈。

栈由一个个栈帧组成,每个栈帧大体都包含四个区域:局部变量表、操作数栈、动态连接、返回地址

数据结构上与栈类似,两者都是先进后出的数据结构,只支持出栈和入栈两种操作。
在这里插入图片描述

局部变量表

方法中定义的局部变量以及方法的参数存放在局部变量表中

它是一个32位的长度,主要存放基础数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型

它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置。如果是局部的一些对象,如Object对象,则只需要存放它的一个引用地址即可。

局部变量表中的变量不可直接使用,如需要使用的话,必须通过相关指令将其加载至操作数栈中作为操作数

操作数栈

以压栈和出栈的方式存储操作数。主要作为方法调用的中转站使用,用于存放方法执行过程中产生的中间计算结果。另外,计算过程中产生的临时变量也会放在操作数栈中。

动态链接

动态链接主要服务一个方法需要调用其他方法的场景。每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。

在Java源文件被编译成字节码文件时,所有变量和方法引用都作为符号引用(Symbolic Reference)保存在Class文件的常量池里。

当一个方法要调用其他方法,需要将常量池中指向方法的符号引用转化为其在内存地址中的直接引用。动态链接的作用就是为了将符号引用转换为调用方法的直接引用。

方法返回地址

当一个方法开始执行后,只有两种方式可以退出:一种是遇到方法返回的字节码指令,如return语句。一种是遇见异常,并且这个异常没有在方法体内得到处理。

不管哪种返回方式,都会导致栈帧被弹出。也就是说:栈帧随着方法调用而创建,随着方法结束而销毁。无论方法正常完成还是异常完成都算作方法结束。

程序运行中栈可能会出现两种错误:

StackOverFlowError: 若栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出StackOverFlowError 错误

OutOfMemoryError: 如果栈的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常

程序计数器 (The pc Register)

一个JVM进程中有多个线程在执行,而线程中的内容是否能够拥有执行权,是根据CPU调度来的。

假如线程A正在执行到某个地方,突然失去CPU的执行权,切换到线程B,然后当线程A再获得CPU执行权的时候,怎么能继续执行?这时候就需要在线程中维护一个变量,记录线程执行到的位置,它就是程序计数器。

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。

为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程之间计数器互不影响,独立存储。

程序计数器主要有两个作用:

字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

多线程情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿。

注意:

如果线程正在执行Java方法,则计数器记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的是Native方法,则这个计数器为空

程序计数器是唯一一个不会出现OutOfMemoryError的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

本地方法栈(Native Method Stacks)

本地方法栈是为JVM运行Native方法准备的空间,由于很多Native方法都是用C语言实现的,所以它通常又叫C栈

与虚拟机栈的区别:

如果当前线程执行的方法是Native类型的,这些方法就会在本地方法栈中执行。如果在Java方法执行的时候调用native的方法,则会动态链接到本地方法栈执行

虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。 

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现StackOverFlowErrorOutOfMemoryError两种错误。

直接内存(Direct Memory)

直接内存并不是虚拟机运行时数据区的一部分,也不是JVM规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。

在JDK1.4 中新加入了NIO类,引入了一种基于通道Channel与缓冲区Buffer的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java 堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。

因为避免了在Java堆和Native堆中来回复制数据,在一些场景中能显著提高性能。

本机直接内存的分配不会受到Java堆大小的限制,但是还是会受到本机总内存的大小及处理器寻址空间的限制。

直接内存与堆内存比较

直接内存申请空间耗费更高的性能

直接内存读取IO的性能要优于普通的堆内存

直接内存作用链: 本地IO -> 直接内存 -> 本地IO

堆内存作用链:本地IO -> 直接内存 -> 非直接内存 -> 直接内存 -> 本地IO

其他内存

Code Cache:JVM本身是个本地程序,还需要其他的内存去完成各种基本任务,如JIT编译器在运行时对热点方法进行编译,就会将编译后的方法储存在Code Cache里面

GC功能需要运行在本地线程中,类似部分都需要占用内存空间。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
手把手视频详细讲解项目开发全过程,需要的小伙伴自行百度网盘下载,链接见附件,永久有效。 课程简介 JVMJava 程序的运行环境,学习 JVM,方能了解 Java 程序是如何被执行的,为进一步深入底层原理乃至程序性能调优打好基础。通过学习这门课程,你将掌握:1. JVM 内存结构的组成、各部分功能作用,学会利用内存诊断工具排查内存相关问题;2. JVM 的招牌功能-垃圾回收机制是如何工作的,如何进行垃圾回收调优;3. Java 程序从编译为字节码到加载到运行的全流程,各个阶段的优化处理;4. 了解 Java 内存模型相关知识,见识多线程并发读写共享数据时的问题和 Java 的解决方案。 适应人群 有一定的Java基础,希望提升 Java 内功的人群。 课程亮点 * 系统地学习 JVM 内存结构,垃圾回收、字节码与类加载技术。 * 在内存结构章节,能够学习掌握 JVM内存溢出现象,堆栈内存结构,利用内存诊断工具排查问题。彻底分析 StringTable的相关知识与性能优化,掌握直接内存分配原理和释放手段。 * 在垃圾回收章节,不仅会介绍垃圾回收算法、分代垃圾回收机制,还会重点介绍 G1 垃圾回收器,辨析 Full GC 发生条件,jdk8以来对垃圾回收的优化,以及垃圾回收的调优法则。 * 在字节码与类加载技术章节,会从一个 class 文件开始分析其每一字节的含义。学习字节码指令的的运行流程,字节码指令与常量池、方法的关系。掌握条件分支、循环控制、异常处理、构造方法在字节码级别的实现原理,利用HSDB工具理解多态原理。还会涉及从编译期的语法糖处理,到类加载的各个阶段,直至运行期的各项优化的详细讲解。最后不要错过方法反射优化的底层分析。 * 最后的加餐环节是带着你理解 Java 内存模型:见识多线程读写共享数据的原子性、可见性、有序性,以及很多人解释不清楚的 happens-before 规则。当然还不能少了 CAS 和 synchronized 优化。 主讲内容 第一章:引言 1. 什么是 JVM ? 2. 学习 JVM 有什么用 ? 3. 常见的 JVM 4. 学习路线 第二章:内存结构 1. 程序计数器 2. 虚拟机栈 3. 本地方法栈 4. 堆 5. 方法 6. 直接内存 第三章:垃圾回收 1. 如何判断对象可以回收 2. 垃圾回收算法 3. 分代垃圾回收 4. 垃圾回收器 5. 垃圾回收调优 第四章:类加载与字节码技术 1. 类文件结构 2. 字节码指令 3. 编译期处理 4. 类加载阶段 5. 类加载器 6. 运行期优化 第五章:内存模型 1. Java 内存模型 2. 可见性 3. 有序性 4. CAS 与原子类 5. synchronized 优化
视频目录 第1节说在前面的话 [免费观看] 00:05:07分钟 | 第2节整个部分要讲的内容说明 [免费观看] 00:06:58分钟 | 第3节环境搭建以及jdk,jre,jvm的关系 [免费观看] 00:20:48分钟 | 第4节jvm初体验-内存溢出问题的分析与解决 [免费观看] 00:17:59分钟 | 第5节jvm再体验-jvm可视化监控工具 [免费观看] 00:21:17分钟 | 第6节杂谈 [免费观看] 00:12:37分钟 | 第7节Java的发展历史00:27:24分钟 | 第8节Java的发展历史续00:02:27分钟 | 第9节Java技术体系00:08:46分钟 | 第10节jdk8的新特性00:07:31分钟 | 第11节lanmbda表达式简介00:07:02分钟 | 第12节Java虚拟机-classic vm00:06:06分钟 | 第13节Java虚拟机-ExactVM00:03:35分钟 | 第14节Java虚拟机-HotSpotVM00:04:23分钟 | 第15节Java虚拟机-kvm00:03:04分钟 | 第16节Java虚拟机-JRockit00:04:12分钟 | 第17节Java虚拟机-j900:04:23分钟 | 第18节Java虚拟机-dalvik00:02:20分钟 | 第19节Java虚拟机-MicrosoftJVM00:03:57分钟 | 第20节Java虚拟机-高性能Java虚拟机00:02:58分钟 | 第21节Java虚拟机-TaobaoVM00:03:06分钟 | 第22节Java内存域-简介00:07:56分钟 | 第23节Java内存域-Java虚拟机栈00:12:04分钟 | 第24节Java内存域-程序计数器00:12:54分钟 | 第25节Java内存域-本地方法栈00:02:39分钟 | 第26节Java内存域-堆内存00:05:08分钟 | 第27节Java内存域-方法00:06:32分钟 | 第28节Java内存域-直接内存运行时常量池00:15:53分钟 | 第29节对象在内存中的布局-对象的创建00:21:19分钟 | 第30节探究对象的结构00:13:47分钟 | 第31节深入理解对象的访问定位00:08:01分钟 | 第32节垃圾回收-概述00:06:20分钟 | 第33节垃圾回收-判断对象是否存活算法-引用计数法详解00:14:08分钟 | 第34节垃圾回收-判断对象是否存活算法-可达性分析法详解00:07:09分钟 | 第35节垃圾回收算法-标记清除算法00:04:36分钟 | 第36节垃圾回收算法-复制算法00:14:35分钟 | 第37节垃圾回收算法-标记整理算法和分代收集算法00:05:24分钟 | 第38节垃圾收集器-serial收集器详解00:09:45分钟 | 第39节垃圾收集器-parnew收集器详解00:04:53分钟 | 第40节垃圾收集器-parallel收集器详解00:11:02分钟 | 第41节垃圾收集器-cms收集器详解00:14:58分钟 | 第42节最牛的垃圾收集器-g1收集器详解00:18:04分钟 | 第43节内存分配-概述00:04:23分钟 | 第44节内存分配-Eden域00:22:51分钟 | 第45节内存分配-大对象直接进老年代00:06:42分钟 | 第46节内存分配-长期存活的对象进入老年代00:03:40分钟 | 第47节内存分配-空间分配担保00:04:54分钟 | 第48节内存分配-逃逸分析与栈上分配00:10:32分钟 | 第49节虚拟机工具介绍00:10:27分钟 | 第50节虚拟机工具-jps详解00:11:20分钟 | 第51节虚拟机工具-jstat详解00:09:20分钟 | 第52节虚拟机工具-jinfo详解00:05:03分钟 | 第53节虚拟机工具-jmap详解00:08:48分钟 | 第54节虚拟机工具-jhat详解00:08:10分钟 | 第55节虚拟机工具-jstack详解00:10:19分钟 | 第56节可视化虚拟机工具-Jconsole内存监控00:07:09分钟 | 第57节可视化虚拟机工具-Jconsole线程监控00:12:18分钟 | 第58节死锁原理以及可视化虚拟机工具-Jconsole线程死锁监控00:10:38分钟 | 第59节VisualVM使用详解00:08:03分钟 | 第60节性能调优概述00:11:22分钟 | 第61节性能调优-案例100:23:28分钟 | 第62节性能调优-案例200:10:05分钟 | 第63节性能调优-案例300:12:41分钟 | 第64节前半部分内容整体回顾00:15:41分钟 | 第65节Class文件简介和发展历史 [免费观看] 00:11:26分钟 | 第66节Class文件结构概述 [免费观看] 00:16:50分钟 | 第67节Class文件设计理念以及意义 [免费观看] 00:13:41分钟 | 第68节文件结构-魔数 [免费观看] 00:09:49分钟 | 第69节文件结构-常量池 [免费观看] 00:23:44分钟 | 第70节文件结构-访问标志 [免费观看] 00:11:36分钟 | 第71节文件结构-类索引00:11:26分钟 | 第72节文件结构-字段表集合00:13:21分钟 | 第73节文件结构-方法表集合00:10:06分钟 | 第74节文件结构-属性表集合00:18:23分钟 | 第75节字节码指令简介00:09:18分钟 | 第76节字节码与数据类型00:09:34分钟 | 第77节加载指令00:09:33分钟 | 第78节运算指令00:10:24分钟 | 第79节类型转换指令00:13:42分钟 | 第80节对象创建与访问指令00:09:38分钟 | 第81节操作树栈指令00:03:27分钟 | 第82节控制转移指令00:11:58分钟 | 第83节方法调用和返回指令00:06:37分钟 | 第84节异常处理指令00:09:44分钟 | 第85节同步指令00:07:34分钟 | 第86节类加载机制概述00:07:26分钟 | 第87节类加载时机00:13:15分钟 | 第88节类加载的过程-加载00:15:15分钟 | 第89节类加载的过程-验证00:10:24分钟 | 第90节类加载的过程-准备00:05:40分钟 | 第91节类加载的过程-解析00:14:04分钟 | 第92节类加载的过程-初始化00:19:41分钟 | 第93节类加载器00:22:41分钟 | 第94节双亲委派模型00:17:03分钟 | 第95节运行时栈帧结构00:08:46分钟 | 第96节局部变量表00:20:48分钟 | 第97节操作数栈00:08:36分钟 | 第98节动态连接00:02:56分钟 | 第99节方法返回地址和附加信息00:03:24分钟 | 第100节方法调用-解析调用00:09:49分钟 | 第101节方法调用-静态分派调用00:16:21分钟 | 第102节方法调用-动态分派调用00:09:02分钟 | 第103节动态类型语言支持00:09:27分钟 | 第104节字节码执行引擎小结00:03:38分钟 | 第105节总结与回顾00:10:55分钟

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeDevMaster

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值