网站安全检测软件,网站能搜索到,株洲网站制作公司在哪里,wordpress 获取子页面基本介绍
JVM#xff1a;Java虚拟机#xff0c;用于解释执行Java字节码
jdk#xff1a;Java开发工具包
jre#xff1a;Java运行时环境
C语言将写入的程序直接编译成二进制的机器语言#xff0c;而java不想重新编译#xff0c;希望能直接执行。Java先通过javac把.java…基本介绍
JVMJava虚拟机用于解释执行Java字节码
jdkJava开发工具包
jreJava运行时环境
C语言将写入的程序直接编译成二进制的机器语言而java不想重新编译希望能直接执行。Java先通过javac把.java文件转成.class文件.class文件是字节码文件包含Java字节码然后Java把这个字节码文件在某个具体的平台上执行。此时再通过jvm把上述的字节码转成对应的CPU能识别的机器指令
当前主流的JVMHotSpot VM JVM中的内存区域划分
JVM其实也是一个进程就是在任务管理器中能看到的Java进程 进程运行的过程中要从操作系统申请一些资源典型资源内存申请到的内存空间可以支撑后续Java程序的执行。
比如在Java中定义变量就会申请内存这里的申请就是由jvm完成的
而jvm申请的这一块内存还会根据实际的用途划分出不同的空间区域划分 1.堆代码中new出来的对象对象中持有的非静态成员变量都是在堆里只有一份
2.栈本地方法栈包含了方法调用关系和局部变量虚拟机栈记录了Java代码的调用关系Java代码的局部变量一般提到的栈指的是虚拟机栈可以有n份n与线程相关
这里的堆和栈和数据结构里的不一样
这里的堆和栈都是内存区域而数据结构的堆是一颗二叉树栈是后进先出的数据结构
3.程序计数器空间比较小存储下一条要执行的Java指令的地址有n份
x86的CPU也有一个类似的寄存器eip
4.元数据区保存类的信息和方法的信息 只有1份
类的信息类的名称继承哪个类实现的接口有什么属性属性名字属性类型权限有什么方法方法名字方法参数权限等等
“元数据(meta data)”往往指一些描述性质或者辅助性质的属性 笔试题
class Test{private int n;private static int m;
}main(){Test t new Test();
}
上述代码里的t, n, m各自处于jvm哪个区域
t是一个局部变量引用类型这个变量在栈上
n是Test的成员变量处于堆上
m是static修饰的变量称为类属性存在类对象中也属于元数据区 JVM的类加载机制
类加载Java进程运行的时候需要把.class文件从硬盘读取到内存并进行一系列的校验解析的过程
过程5个步骤
1.加载把硬盘上的.class文件找到打开文件读取文件内容读到的是二进制数据
如何查找对应的文件双亲委派模型一种查找策略
2.验证确保读到的文件是合法的.class文件验证依据Java的虚拟机规范
3.准备给类对象申请内存空间申请到的内存空间里面的默认值是0
4.解析针对类中字符串常量进行处理将常量池中的符号替换成直接引用的过程
class Test{private String s hello;
} 在代码中我们知道s相当于包含了hello字符串常量的地址但是在文件中是不存在地址这样的概念的。地址是内存的地址硬盘里没有地址。
虽然没有地址但是我们可以存储一个类似于地址的偏移量
把hello字符串的开头到文件开头就是一个偏移量 此处文件中填充给s的hello的偏移量就认为是符号引用接下来把.class文件加载到内存中先把hello这个字符串加载到内存中此时“hello”就有地址了s里面的值就可以替换成当前“hello”真实的地址了可以直接引用这个地址了
5.初始化针对类对象完成后续的初始化要执行代码块的逻辑甚至会触发父类的加载 双亲委派模型重点
JVM中的类加载操作有一个专门的模块类加载器
作用给定全限定类名也就是带有包名的类名比如java.lang.String就是一个全限定类名能找到.class文件
默认有三个
BootstrapClassLoader负责查找标准库的目录
ExtensionClassLoader负责查找扩展库的目录
ApplicationClassLoader负责查找当前项目的代码目录以及第三方库的目录
上述三个类加载器存在父子关系这个父子关系类似于二叉树有一个指针parent指向自己的父类加载器 双亲委派模型的工作过程
1.从ApplicationClassLoader作为入口先开始工作
2.ApplicationClassLoader不会立即搜索自己负责的目录会把搜索的任务交给自己的父亲
3.代码进入到ExtensionClassLoader的范畴ExtensionClassLoader也不会立即搜索自己负责的目录会把搜索任务交给父亲
4.BoostrapClassLoader没有父亲没办法推脱搜索任务了才会真正搜索自己负责的标准库目录。通过全限定类名尝试在标准库目录中找到符合要求的.class文件
找到了就直接进入打开文件和读文件的流程如果没到找返回给孩子类加载器继续尝试加载
5.ExtensionClassLoader收到交回的任务后在自己负责的扩展库目录搜索找到了进入后续流程没找到再丢给自己孩子
6.ApplicationClassLoader收到交回的任务后自己进行搜索负责的目录。再找不到就抛出ClassNotFoundException 异常 上述执行顺序的好处
1确保几个类加载器之间的优先级
2用户自定义的类不会被jvm加载可以防止自定义类不小心和标准库中的类名字重复 JVM的垃圾回收机制GC机制
基本情况
这个机制不需要程序员手动释放内存。程序回自动判断某个内存是否会继续使用如果内存后续不用了就会自动释放掉。
垃圾回收中一个重要问题STWstop the world问题——触发垃圾回收的时候可能会使当前程序的其他业务逻辑被暂停
垃圾回收内存的话那内存里面几个区域里面情况如何
1程序计数器 -- 不需要GC
2栈 -- 不需要GC因为局部变量在代码块执行结束之后自动销毁
3元数据区/方法区 -- 不需要GC因为一般都是涉及类加载而不是类卸载
4堆 -- GC的主要工作区域
所以垃圾回收回收内存不如说是回收对象对象也是回收的单位 工作机制
1.识别出垃圾
判定哪些对象不再使用哪些对象还在使用
在Java中的对象一定要通过引用的方式来使用除非匿名对象。如果一个对象没有任何引用指向它就可以认为无法被代码引用就可以作为垃圾了
void func(){Test t new Test();t.do();
}
这里通过new Test在堆上创建了对象与此同时在栈上也存储下这个局部变量
当执行到 “}” 的时候t这个局部变量在栈中自动销毁。上面的new Test()对象就没有引用指向它了。此时这个对象就成为了垃圾
如果代码复杂一点呢
Test t1 new Test();
Test t2 t1;
t3 t2;
t4 t3;此时会有很多引用指向new Test同一个对象需要确保所有的引用都销毁了才能把Test对象视为垃圾。如果代码再复杂每个引用的生命周期各不相同那怎么办呢
解决办法
1引用计数给每个对象安排一个额外的空间空间里要保存当前这个对象有几个引用 每次有一个引用指向这个对象引用计数就1制空或者删除一个引用引用计数就-1
此时有专门的扫描线程去获取当前每个对象的引用计数情况如果发现对象的引用计数为0说明这个对象可以被释放了
这个方法虽然没有在JVM中使用但是广泛应用于其他语言的垃圾回收机制中比如python和PHP
问题一每个对象分配到的计数器消耗了额外的内存空间对象数目一多空间资源容易不足
问题二引用计数可能会产生“循环引用的问题” 此时两个对象引用计数都不是0不能被GC回收掉但是这两个对象又无法使用 -- 类似于死锁 2可达性分析JVM用的是这个
本质上用时间换空间。在写代码的时候会定义很多变量就可以从这些变量作为起点尝试进行遍历沿着这些变量中持有的引用类型的成员再进一步地往下进行访问所有能被访问的对象就不是垃圾了剩下的遍历一圈也访问不到的对象就是垃圾 虽然这个代码中只有一个root的引用但是7个结点的对象都是可达的。JVM中存在扫描线程会尽可能多的去遍历访问对象
如果root.right null的话a跟c之间就会断开那么按照上述方法遍历的操作就无法访问到c和f了此时c和f节点对象就不可达不可达就变成垃圾了 2.把标记为垃圾的对象的内存空间进行释放
释放方式
1标记 - 清除
把标记成垃圾的对象直接释放掉一般不使用 产生的问题内存碎片 -- 小的但是离散的空闲内存空间
会导致后续的内存申请失败。因为我们的内存申请时一次性申请一个连续的空间比如我们申请1M的内存空间此时的1M字节都是连续的
如果有很多内存碎片就可能导致空闲空间总和超过1MB但是没有比1MB大的连续空间申请就会失败 2复制算法
核心是不直接释放内存而是把内存划分成为两半
把不是垃圾的对象复制到内存的另一半里接下来就把左侧的空间原来垃圾存在的空间整体释放掉
比如我们要删掉对象2和4我们会把不需要删除的1和3复制一份到右半边内存 然后把左半边全删掉 优点规避内存碎片问题
缺点1总的可用内存变少2如果每次要复制的对象很多复制的开销很大所以这个算法适用情况当前这轮GC中要删掉的对象很多存活的对象很少 3标记 - 整理
类似顺序表删除中间的元素搬运思想
比如下面要删除136 接着把24578往前搬运 优点解决内存碎片问题不需要过多浪费内存空间
缺点复制开销很大 4JVM采用的综合方案分代回收重点
引入概念对象的年龄初始年龄为0
JVM中有专门的线程负责周期性扫描或释放。一个对象如果被线程扫描了一次发现是可达了该对象的年龄1
JVM会根据对象年龄的差异把整个堆内存分成两个大的部分分别为新生代年龄小的对象和老年代年龄大的对象 具体流程复制算法标记 - 整理
a当代码中new处理一个新的对象这个对象就被放在伊甸区
一个经验规律大部分伊甸区的对象是朝生夕死的活不过第一轮GC
b第一轮GC扫描之后少数幸存的对象就会通过复制算法拷贝到生存区后续GC扫描线程继续扫描生存区中大部分对象也会在扫描中被标记为垃圾少数存活的会被拷贝到另一半的生存区中每经历一轮扫描生存的对象年龄1
c如果这个对象在生存区中经过若干轮GC之后还存活着JVM会认为这个对象的生命周期很长就会将其从生存区拷贝到老年代
d老年代的对象也要参与扫描但是被扫描的频率大大降低
e对象在老年代寄掉了JVM就将其释放了
常使用的垃圾收集器GMS, G1和ZGC