1. JVM的位置
2.JVM的体系结构
3.类加载器
1.虚拟机自带的加载器
2.启动类(根)加载器
3.扩展加载器
4.应用程序加载器
JVM中提供了三层的ClassLoader:
- Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。
- ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。
- AppClassLoader:主要负责加载应用程序的主函数类
双亲委派
- 类加载器收到类加载的请求
- 将这个请求向上委托给父类加载器去完成,一直向上委托,知道启动类加载器
- 启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则,抛出异常,通知子加载器进行加载
- 重复步骤3
Native 关键字:
- Java平台有个用户和本地C代码进行互操作的API,称为Java Native Interface (Java本地接口)。
- native 是用做java 和其他语言(如c++)进行协作时用的也就是native 后的函数的实现不是用java写的
- native是与C++联合开发的时候用的!
- java自己开发不用的!使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。
- JNI 调用 C 流程图
4.PC寄存器
程序计数器:
- 每个线程都有一个程序及耍起,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向像一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计
5.方法区
Method Area 方法区
方法是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享空间
静态变量、变量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是 实例变量存在堆内存中,和方法区无关。
6.栈
- 栈:先进后出、后进先出 例如:桶 队列:先进先出 (FIFO:First Input FirstOutput)
喝多了吐就是栈,吃多了拉就是队列
- 栈内存主馆程序的运行,生命周期和线程同步;线程结束,栈内存也就是释放,对于栈来说,不存在垃圾回收问题,一旦线程结束,栈就Over!
- 主要存放:8大基本数据类型+对象引用+实例的方法
- 运行原理:站+帧
7.堆
- 三种JVM
- Sun公司(Java HotSpot™ 64-Bit Server VM (build 25.5-b02, mixed mode)) 常吃用
- BEA
JRockit
- IBM
J9 VM
简述: Heap,一个JV只有一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后,一般会把什么 东西放到堆中?类、方法、常量、变量~,保存我们所有引用类型的真实对象;
堆内存中还要细分为三个区域:
- 新生区(伊甸园区)Young/New
- 养老区 old
- 永久区 Perm (1.8后变为
元空间
)- 这个区域常驻内存的。用来存放JDk自身携带的Class对象。Interface元数据,存储的是java运行是的一些环境或类信息,这个区域不存在垃圾回收!关闭VM虚拟就会释放这个区域的内存
- 一个启动类,加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成反射类。不断的被加载。直到内存满,就会出现OOM;
- jdk1.6之前:永久代,常量池在方法区 jdk1.7:永久代,但是慢慢的退化了,
去永久代
,常量池在堆中 jdk1.8:无永久代,常量池元空间
元空间:逻辑上存在;物理上不存在
-Xms 设置初始化内存分配大小
-Xmx 设置最大分配内存
-Xx:+PrintGCDetails //打印GC垃圾回收信息
-xx:+HeapDumpOnOutOfMemoryError //oom DUMP
示例:-Xms1024m -Xmx1024m -xx:+HeapDumpOnOutOfMemoryError
8. GC:垃圾回收
- JVM在进行GC时,并不是对这三个区域统一回收。大部分时候,回收都是新生代~
- 新生代、幸存区、老年区
- GC分两种:轻GC(普通的GC)、重GC(全局GC)
GC题目:
- JVM的内存模型和分区~详细到每个区放什么
- 堆里面的分区有哪些? Eden,from ,to, 老年代,说说他们的特点!
- GC的算法有哪些?标记清除法、标记整理(标记压缩)、算法复制、引用计数器,怎么用的?
- 轻GC和重GC分别在什么时候发生?
GC垃圾回收的几种算法
一:引用计算算法
- 简述: 给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1,任何时刻计算器为0的对象就是不可能再被使用的。
- 优: 实现简单,判定效率相对高;
- 缺: 它很难解决对象之间相互循环引用的问题,对于循环引用的对象无法进行回收;
二: 复制算法
- **简述:**将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。在对象达到默认值
15
是,将会进入老年代 - 优: 没存的碎片,与标记-清除算法相比,复制算法是一种相对高效的回收方法,适合做新生代的GC
- 缺: 空间浪费
三:标记压缩清除算法
标记-清除算法是现代垃圾回收算法的思想基础。标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象;然后,在清除阶段,清除所有未被标记的对象。
- 简述:
- 优点: 不需要额外的空间
- 缺点: 效率比较低(递归与全堆对象遍历);标记清除算法需要2次扫描,标记压缩清除算法需要3次,严重浪费时间,会产生内存碎片。
内存效率:复制算法 > 标记算法 > 标记压缩算法(时间复杂度)
内存整齐度:复制算法 = 标记压缩算法 > 标记清除算法
内存利用率:标记压缩算法 = 标记清除算法 > 复制算法
没有最好的算法,只有最合适的算法—>分代收集算法
JMM
- 什么是JMM?
JMM:(Java Memory Model的缩写), java内存模型 - 作用?
缓存一致性协议,用于定义数据读写的规则(遵守,找到这个规则)
JMM定义了线程工作内存和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main memory)中,每个线程都有一个私有的本地内存(Local Memory)
volatile关键字:
- CPU中运行的线程从主存中拷贝共享对象obj到它的CPU缓存,把对象obj的count变量改为2。但这个变更对运行在右边CPU中的线程不可见,因为这个更改还没有flush到主存中:要解决共享对象可见性这个问题,我们可以使用java volatile关键字或者是加锁
- 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。
JMM面试题
- 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。
谈谈JMM
JMM(java内存模型)Java Memory Model,本身是一个抽象的概念,不是真实存在的,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段、静态字段和构成数组对象的元素)的访问方式。
JMM同步规定
(1)线程解锁前,必须把共享变量的值刷新回主内存
(2)线程加锁前,必须读取主内存的最新值到自己的工作内存
(3)加锁解锁是同一把锁
由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方成为栈空间),工作内存是每个线程的私有数据区域,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作空间,然后对变量进行操作,操作完成再将变量写回主内存 ,不能直接操作主内存中的变量,各个线程中的工作内存储存着主内存中的变量副本拷贝,因此不同的线程无法访问对方的工作内存,此案成间的通讯(传值) 必须通过主内存来完成,其简要访问过程如下图: