我是
方圆
,一边做课程设计,一边更博客
目录
1. native关键字(了解)
Java无法直接访问系统的底层
(我们熟悉的启动线程中start0()方法就是被native关键字修饰),所以,引入native关键字对Java实现扩展,通过JNI(Java Native Interface)
接口调用其他语言(如C和C++),实现对底层的访问。
2. 方法区
方法区中存在静态变量
,常量
,类信息
,运行时常量池
存在方法区中,但是实例变量存在堆内存中,和方法区无关。(static,final,Class,常量池)
3. 栈
栈中保存的是八大基本类型,对象的引用
4. 类的加载过程
类的生命周期如下图所示
加载
在加载阶段,我们可以参考java.lang.ClassLoader中的loadClass()方法,会在方法区
中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口验证
这个阶段主要确保了Class文件的字节流中包含的信息符合虚拟机的要求,并且不会危害虚拟机的安全。验证是非常重要的,但是不是必须的,它对程序运行期没有影响
准备
准备阶段是正式为类变量(static)
分配内存并设置初始值的阶段,这些变量所使用的内存都在方法区
中分配。解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。初始化
这个阶段是类加载过程的最后一步。在准备阶段,我们已经对类变量赋值过(初始0值),而在这个初始化阶段,则根据我们写的代码去初始化变量和其他资源。或者说,初始化阶段是执行类构造器<clinit>()
方法的过程。<clinit>()方法
是由编译器自动收集类中所有的类变量赋值语句
和静态代码块中的赋值语句
所产生的。
虚拟机会保证一个类的类构造器<clinit>()
在多线程环境中被正确的加锁、同步
,如果多个线程同时
去初始化一个类,那么只会有一个线程
去执行这个类的类构造器<clinit>()
,其他线程都需要阻塞等待,直到活动线程执行<clinit>()
方法完毕。需要特别注意的是,其他线程虽然会被阻塞,但是当<clinit>()
执行完毕,其他线程被唤醒之后不会再去执行这个方法,因为在同一个类加载器下,一个类型只会被初始化一次
。
5. 对象的实例化过程
实例化一个类的对象,是一个典型的递归过程,如下图所示。
在准备实例化一个对象前,首先会准备实例化该类的父类,如果该类的父类还有父类,那么还会向上实例化,直到递归到Object类。实例化Object类之后,再依次向下对各类进行实例化。
5.1 举一个小例子
Student student = new Student();
- 当虚拟机遇到一条new命令的时候,先检查方法区中
是否存在该对象的类信息
,并且检查该类是否被加载、解析和初始化
(1)如果方法区中没有该类的信息,那么会抛处ClassNotFoundException
(2)若以上的检查全部通过,则进入下一步工作 - 类加载完成后,虚拟机将为新生对象分配内存
- 从堆中划分一块儿相应大小的内存给新的对象
- 为新对象的成员变量附上初始值
- 设置并保存对象头信息(Object Header),对象头信息包括该对象是哪个类实例、对象的哈希码、对象的 GC 分代年龄等信息
- 一般来说,执行 new 指令之后会接着执行 方法,把对象按照程序员规定的构造函数进行初始化
6. 堆
堆
中存放的是对象,包含年轻代和老年代,永久代
(在jdk1.8被元空间取代)中包含方法区
6.1 分代概念
新生成的对象首先放到年轻代Eden区,当Eden空间满了
,触发Minor GC,存活下来的对象移动到Survivor0区,Eden再次填满后
触发执行Minor GC,Survivor0区存活对象移动到Suvivor1区,这样保证了一段时间内总有一个survivor区为空(空的为to,非空的为from)。经过15
次Minor GC仍然存活的对象移动到老年代。
老年代
存储长期存活的对象,占满时
会触发Major GC=Full GC,GC期间会停止所有线程等待GC完成,所以对响应要求高的应用尽量减少发生Major GC,避免响应超时。
Minor GC
: 清理年轻代Major GC
: 清理老年代Full GC
: 清理整个堆空间,包括年轻代和永久代
6.2 为什么分代?
将对象根据存活概率进行分类,对存活时间长的对象,放到固定区,从而减少扫描垃圾时间及GC频率。针对分类进行不同的垃圾回收算法,对算法扬长避短。
6.3 为什么survivor分为两块相等大小的幸存空间?
主要为了解决碎片化。如果内存碎片化严重,也就是两个对象占用不连续的内存,已有的连续内存不够新对象存放,就会触发GC。
6.4 JVM堆内存中常用参数
参数 | 描述 |
---|---|
-Xms | 堆内存初始化大小 |
-Xmx | 堆内存最大允许大小,一般不超过物理内存的80% |
-XX:+PrintGCDetail | 打印GC详细信息 |
-XX:PermSize | 非堆内存初始大小,一般设置200M |
-XX:MaxPermSize | 非堆内存允许的最大大小 |
-Xns | 年轻代内存初始化大小 |
-Xmn | 年轻代内存允许的最大内存大小 |
-XX:SurvivorRatio=8 | Eden区与Survivor区的容量比值,默认为8 |
-Xss | 栈内存大小 |