认识JVM和运行时内存
基本概念:JVM运行Java代码的假想计算机,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收、一个堆、一个存储方法域。
JVM与硬件没有直接交互,运行在操作系统之上。
运行过程:
-
Java源文件---->编译器---->字节码文件
-
字节码文件---->JVM解释器---->机器码
内存区域:
运行时数据区:方法区、堆、栈、本地方法栈、计数器
1、线程
JVM允许一个应用并发执行多个线程。JVM中的Java线程与操作系统线程有直接的映射管理。
-
当线程本地存储、缓冲区分配、栈等准备好之后,就会创建一个操作系统原生线程。
-
当原生线程初始化完毕,就会调用Java线程的run方法。
-
Java线程结束时,会释放原生线程和Java线程的所有资源。
JVM后台线程:
-
虚拟机线程:
-
周期性任务线程:负责定时器事件
-
GC线程:垃圾回收活动
-
编译器线程:将字节码动态的编译成本地相关的机器码
-
信号分发线程:接收发送到JVM的信号并调用JVM方法
2、内存区域
主要包括:
-
线程私有:计数器、栈、本地方法区
-
线程共享:堆、方法区
-
直接内存(不收JVM GC管理)
生命周期:
-
线程私有数据区域生命周期与线程相同,依赖用户线程的启动、结束、创建、销毁。
-
线程共享区域随JVM的启动/关闭而创建/销毁。
-
直接内存不属于运行时数据区的一部分。
2.1 程序计数器(线程私有)
当前线程所执行的字节码的行号指示器。
特点:唯一一个没有规定任何OOM的区域
2.2 虚拟机栈(线程私有)
java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用到执行完成,就对应了一个栈帧在虚拟机栈中入栈和出栈的过程。
2.3 本地方法区(线程私有)
栈为执行Java方法服务,而本地方法栈则为执行Native方法服务。
2.4 堆(线程共享)
创建的对象和数组都保存在Java堆内存中,也是垃圾收集器进行垃圾收集的最重要的区域。GC采用分代收集算法,因此堆从GC的角度还可以分为:新生代(Eden、FS、TS)和老年代。
2.5 方法区/永久代(线程共享)
用于存储JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,
3、运行时内存
堆空间内部,新生代1/3,老年代2/3
新生代内部,Eden:from:to=8:1:1
新生代:
Eden区:Java新对象的出生区,新对象太大会直接分配到老年代。Eden区内存不够时,触发MinorGC。
MinorGC过程:复制-清空-互换
复制:Eden区和from区存活的对象赋值到to区,年龄+1,达到老年标准就复制到老年区;
清空:清空Eden和from;
互换:to和from互换,to成为下一次GC的from区;
老年代:
老年代内存不足时触发MajorGC,或者新对象太大也会提前触发一次MajorGC。
MajorGC采用标记清除算法:扫描,标记存货的对象,回收没有标记的对象。会产生内存碎片,进行合并。
老年代也装不下的时候,抛出OOM异常。
永久代:存放Class、元数据信息,Class在加载的时候被放入永久区域。
JAVA8,永久代被移除,由元空间取代。最大的区别是,元空间不在JVM中,而是使用本地内存,这样加载的类的元数据由系统的实际可用空间来控制。