程序 = 数据结构 + 算法
1.JVM体系结构
JVM是在操作系统上面的,使用c/c++写的,属于JRE
JVM组成:方法区(Method Area),Java栈(Java stack)、本地方法栈(Native Method)、堆(Heap)、程序计数器
JVM调优99%是在方法区和堆,其中的99%是在堆中
栈一般存放引用,放对象在堆中的地址
堆一般存放new的对象和数组
类的加载过程
类是模板(只有1个),对象是具体的(很多个)
2.双亲委派机制,主要是保证安全
类加载器:App(应用程序加载器)–>Ext(扩展加载器)–>Boot(根加载器)
双亲委派(先向上委派,再向下委派;也就是请求向上,加载向下):
- 类加载器收到类加载的请求,类加载器保证线程安全
- 将请求向上委派给父类加载器,一直向上委派,直到跟加载器
- 启动加载器检查是否能加载当前这个类,能加载就结束,使用当前的加载器,不行就向下通知子加载器加载
- 重复3,直到在App(应用程序加载器)中查找,
- 如果没找到就报异常ClassNotFound
- 可以想象啃老族,父母没有才会自己动手
如果java的同包同类名的类(String):实例化会怎么样
报异常:找不到main方法,因为双亲委派机制(在root根加载器就查找到了这个类)
为啥程序中获取不到跟加载器的信息
- 因为java底层是用C、C++写的,java调用不到
3.沙箱安全机制
Java安全模型的核心,限制程序运行的环境,将Java代码限定在JVM特定的运行环境中,严格限制代码对本地资源访问(主要限制系统资源访问)
双亲委派机制实现沙箱安全机制
4.Native和方法区
- 带Native关键字的,说明Java作用范围达不到了,会去调用底层C语言的库,进入本地方法栈,调用本地方法的本地接口(JNI)
- 方法区:被所有线程共享,所有定义的方法的信息都保存在该区域,此区域属于共享区域
静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关
JNI:扩展Java的使用,融合不同的编程语言为Java所用(本地方法栈登记native方法,执行的时候通过这个调用本地方法接口JNI)
Java的本质是值传递
5.栈和队列
- 栈:先进后出(main方法):喝多了吐
- 主管程序的运行,生命周期和线程同步;线程结束,栈内存释放,不存在垃圾回收机制(线程级)
- 存放:8大基本类型+对象引用+实例的方法
- 队列:先进先出(FIFO):吃多了拉
- 栈溢出:就是两个方法互相调用对方,一直往栈里面压,导致栈被压满了
6.堆
Heap,一个JVM只有一个堆,堆内存可以调节 (调优)
存:类、方法、常量、变量、引用类型的真实对象
分区
- GC垃圾回收主要是在伊甸园区和养老区(jdk8以后永久存储区叫
元空间
)
通过代码获取堆空间大小
发现总空间=eden+old,没有meatspace的空间,即逻辑上元空间存在堆中,但物理上不存在堆中
GC调优主要是堆:
VM options:-Xms8m -Xmx8m -XX:+PrintGCDetails
PrintGCDetails
:印GC垃圾回收信息
Xms
:设置初始化内存分配大小,1/64 对应于Xmax
Xmax
:设置最大分配内存:默认1/4 对应电脑内存
7、GC垃圾回收
- 新生区:伊甸园区(Eden)、幸存0区(from)、幸存1区(to),from和to会相互转化,谁是空的谁就是to
当一个对象经过15(可设置)次GC都还没死,就会进入养老区
- 养老区
- 永久区(JDK8以后改成了元空间):常驻内存,用来存放JDK自身携带的Class对象,方法区,Interface元数据以及Java运行时的一些环境和类对象,不存在垃圾回收,关闭虚拟机释放内存
GC垃圾回收,主要是在伊甸园区和养老区
内存分析插件
内存快照分析工具:能看到第几行代码出错(Jprofiler
,eclipse的MAT)
- 分析Dump内存文件,快速定位内存泄露
Xms1m -Xmax8m -XX:+HeapDumpOnOutOfMemoryError
- 打印OOM栈溢出的信息,生成profile文件
- 获得堆中的数据及大的对象
7.题目
- JVM的内存模型和分区~详细到每个区放什么?
- 堆的分区:Eden、from、to、老年区,特点?
- GC的算法?
- 引用计数器:给每个对象都配对一个计数器,引用就+1,为0就销毁,会造成很大消耗
- 复制算法:主要用在新生区(from和to),如果from和to都有对象,就可以把from的对象复制到to中,然后from变成to(to必须一直为空,
谁空谁是to
);默认值:当一个对象经历了15次gc还没死,就进入养老区- 好处:没有内存碎片
- 坏处:浪费了一半内存(多了一半是to区)
- 复制算法使用场景:对象存活率较低的区域
- 标记清除法:对有引用的对象标记,对没有标记的对象清除
- 优点:不需要额外的空间
- 缺点:两次扫描,浪费时间,会产生内存碎片
- 标记压缩:再次扫描,将存活的对象移动到一端,解决标记清除的内存碎片问题 (多了移动的成本) (再优化:多进行几次GC,再进行压缩)
没有最优的方案,永远只是时间或空间的权衡。
现在又不缺空间,所以优先复制算法
GC:分代收集算法(年轻代:复制算法,老年代:标记清除算法+压缩混合)
- 轻GC(新生和养老区)和重GC(**和永久区)分别发生的时候?
学习新东西方法
比如说JMM
-
- 什么是JMM?
-
- 它是干什么的?
-
- 它该如何学习?