一、jvm框架
操作系统运行再硬件之上,虚拟机运行再操作系统之上。
1、类加载器ClassLoader
负责加载class文件,class文件在文件开头有特定的文件标示cafe babe,将class文件字节码内容加载到内存中,并且将这些内容转换成方法区中的运行时数据结构并且ClassLoader只负责class文件加载,是否可以运行,由Execution Engine决定,它负责解释命令提交操作系统执行。
类加载器的种类(3+1=4种):
虚拟机自带的加载器:启动类加载器(Bootstrap) C++
扩展类加载器(Extension) Java
用户自定义的加载器:应用程序类加载器AppClassLoader
3+1:3为系统自带+1为可以自己继承加载器classLoader,classLoader为一个抽象类
双亲委派机制(为什么不能写(命名相同)Java自带的类)
我爸是李刚,有事找我爹。首先从根开始,一级一级往下。
自己写一个Public class String 包为java.lang。运行报错,找不到main方法。首先使用bootstrap执行了,成功了但是它里面没有main方法。
java:委派性、安全性。
沙箱机制的基本组件:1、字节码校验器
2、类装载器
2、本地接口
作用:融合不同的编程语言为Java所用,融合C/C++程序。
线程和语言没有关系,和操作系统有关。如果多次使用thread.start(),会报错。
线程.start之后不是马上启动,真正启动的是start0,待cup和内存调度。
native 出现表示Java语言已经到此,再执行多线程就得接入第三方的操作系统库或者是C语言函数库。专门留给C语言。
private native void start0(); 只有方法的声明没有方法的实现,方法的实现交给底层的操作。
native特殊的方法放入特殊的栈中,一般的方法放入Java栈中。
多线程的六个状态:new、runnable、blocked、waiting、timed_waiting、terminated。
3、PC寄存器(Program Computer Register)
它就是一个指针,指着这个方法运行完了,下一个要运行的方法是谁。存储指向吓一跳指令的地址。
占用内存很小,不存在内存回收机制。
每一个线程都有一个程序计数器,他是线程私有的。它是当前线程所执行的字节码的行号指示器。
如果执行一个native方法,则他的计数器为空。
4、方法区
所有线程共享,存在垃圾回收。
(1)它存储了每个类的结构信息,例如字段、方法数据、构造函数、普通方法的字节码内容。
(2)方法区是规范,在不同的虚拟机里实现是不一样的,最典型的是永久代和元空间。
实例变量存在堆内存中,与方法区无关。实例方法:类中自带的方法,static为全局的不属于自带。
5、栈(stack)
(1)栈管运行,堆管存储 程序 = 算法 + 数据结构 程序 = 框架 + 业务逻辑
队列:先进先出(FIFO)
栈:先进后出(FILO)
栈也叫栈内存,主管java程序的运行,是在线程创建时创建,他的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说,不存在垃圾回收问题,线程一结束该栈就结束。
(2)栈存储哪些东西 :8种基本类型的变量 + 对象的引用变量 + 实例方法都是在函数的栈内存中分配
栈帧:Java的方法压入栈中就叫栈帧。
栈帧中的主要三种类数据:
本地变量(Local Variables):输入参数和输出参数以及方法内的变量;如子弹外面的弹壳。
栈操作(Operand Stack):记录出栈、入栈的操作。
栈帧数据(Frame Data):包括类文件、方法等等。
栈的运行原理:
栈中的数据都是以栈帧的形式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法和运行期数据的数据集,当一个方法被调用就产生了一个栈帧,并被压入栈中,执行完毕后再依次弹出。
每个方法的执行同时都会创建一个栈帧,用与存储局部变量表、操作数栈、动态链接、方法出口等信息,每个方法从调用直至执行完毕的过程,就对应这栈帧在虚拟机中入栈到出栈的过程。
当方法一直往栈中加时;stackOverflowError 栈溢出 它不是异常是错误
6、栈、堆、方法区之间的关系
栈需要执行方法,堆中存放实例对象、方法区中存放类的模板
7、堆(heap)
逻辑上三部分,物理上两部分:新生加养老
(1)新生代 new : 1>伊甸区 gc就是说的这里 GC = YGC = 轻量级GC
2>幸存0区 S0 = from区
3>幸存1区 S1 = to区
from区和to区,他们的位置和名分,不是固定的,每次GC后会交换;GC之后有交换,谁空谁是to区。先从eden区复制活下来的,然后Eden全杀,触发YGC。
(2)老年代 old Full GC = FGC Full GC多次,发现养老区空间没办法腾出,OOM异常(OutOfMemory)
(3)永久代7元空间8
动态分析:
实际而言,方法区和堆一样,是线程共享的内存区域,它用于虚拟机加载的:类信息 + 普通常量 + 静态常量 + 编译后的代码等。虽然逻辑上永久代是堆的一部分,但是实际上,它是分开的。永久代是用来实现方法区的。
二、堆参数调优
初始内存和最大内存一样大,避免GC和应运程序争抢内存,理论时的分时和分组忽高忽低。
OutOfMemoryError //堆内存超出
参数:-Xms 设置初始分配大小,默认为物理内存的1/64
-Xmx 最大分配内存,默认为物理内存的1/4
-XX:+PrintGCDetails 输出详细的GC处理日志
查看GC日志信息:
GC四大算法
1、GC算法总体概述
JVM在进行GC时,并非每次都对上面三个内存区域一起回收的,大部分时候回收的都是指新生代。
因此GC按照回收的区域又分了两种类型,一种是普通GC(minor GC),一种是全局GC(major GC or Full GC)
Minor GC和Full GC的区别
普通GC(minor GC):只针对新生代区域的GC,指发生在新生代的垃圾收集动作,因为大多数Java对象存活率都不高,所以Minor GC非常频繁,一般回收速度也比较快。
全局GC(major GC or Full GC):指发生在老年代的垃圾收集动作,出现了Major GC,经常会伴随至少一次的Minor GC(但并不是绝对的)。Major GC的速度一般要比Minor GC慢上10倍以上
2、四大算法
(1)引用计数法
(2)复制算法(copying)
优点:复制算法不会产生内存碎片。
缺点:耗空间。
(3)标记清除(Mark-Sweep)
优点:不需要额外空间。
缺点:两次扫描,耗时严重;会产生内存碎片(内存不连续)。
(4)标记压缩(Mark-Compact) = 标记清除+压缩 = 标记整理
首先进行标记清除,然后再一次扫描,往一端滑动存活对象。
优点:没有内存碎片
缺点:需要移动对象的成本,浪费时间。
(5)标记清除-压缩 :多次进行清除后再进行压缩,是最好的,但是不在四大基本算法中。
(6)Java9之后会有更好的G1.
内存效率上:复制>标记清除>标记整理
内存整齐度:复制>标记整理>标记清除
内存利用率:标记整理=标记清除>复制
GC是什么(分代收集算法):次数上频繁收集Young区
次数上较少收集Old区
基本不动元空间
没有最好的算法,根据每一代垃圾的特性用不同的算法,新生代用复制,年老区用标记清除和标记整理算法。
三、JMM(java内存模型)
Java Merory Model:本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范通过规范定制了程序中各个变量的访问方式。
由于jvm运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方成为栈空间),工作内存是每个线程的私有数据区域,而JAVA内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可访问,但线程对变量的操作(读写赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到自己的工作空间,然后对变量进行操作,操作完成再将变量写回主内存,不能直接操作主内存中的变量。
(1)可见性
(2)原子性(不可分割)
(3)有序性