
JVM分为两个子系统,两个组件
一个子系统是Class loader类装载系统,另一个子系统是Execution Engine执行引擎
一个组件是Runtime data area 运行时数据区,Native Interface 本地接口
Class loader:根据给定的全限定类名来装载class文件到运行时数据区的方法区
执行引擎执行classses中的指令
本地接口,用来和其他语言交互,Android的JNI
运行时数据区:就是JVM的内存
作用:通过编译器将java文件转成字节码,ClassLoader将字节码加载到内存(运行时数据区),字节码是JVM的一套指令集规范,不能直接给底层操作系统执行,需要经过执行引擎将字节码翻译成底层系统指令,再交给CPU去执行,这个过程需要调用本地接口来实现
编写的java源码.java文件,通过javac转成字节码.class文件,classLoader将这些.class文件加载到JVM内存,就是运行时数据区,classLoader将.class文件的二进制数据加载到jvm内存的方法区,然后在堆区创建对应的类对象,用来封装类在方法区的数据结构。
JVM内存,就是只JVM的运行时数据区此区域包含:
线程数据共享区:方法区,堆区
线程数隔离区:虚拟机栈,方法栈,程序计数器
程序计数器:当前程序所执行的字节码行号指示器,字节码解析的工作就是通过改变计数器的值,一行一行的解析字节码指令,分支、循环、跳转、异常处理,线程恢复等基础功能都依赖这个计数器
虚拟机栈:存储局部变量表、操作数栈、动态链接、方法出口
本地方法:与虚拟机栈作用一样,只是为了调用native方法服务
堆区:jvm内存中最大的一块,被所有线程共享,几乎所有对象的实例都在这里分配内存
方法区:用来存储已经被加载到JVM内存的类信息,常量,静态变量,即时编译后的数据
浅拷贝:内存地址是同一个,增加一个指针指向此内存地址
深拷贝:增加一个指针且申请一个新的内存地址,指针指向的是新的内存地址
浅复制:复制被指向的内存地址,如果原地址发生变化,浅复制出来的对象也会发生变化
深复制:在计算机中开辟一块新的内存地址用来存放复制的对象
堆栈区别:
物理地址:
堆的物理地址是不连续的,栈的物理地址是连续的,栈使用的是数据结构中的栈,先进后出原则,性能更快
内存分别:
堆的地址是不连续的,分配内存是在运行时确认,大小不固定,堆一般远大于栈
栈是连续的,分配内存是在编译期间,大小固定
存放的内容:
堆存放的是对象的实例和数组,更关注数据的存储
栈存放:局部变量,操作数栈,返回结果,更关注方法的执行
静态变量放在方法区,也就是线程共享,静态对象放在堆区,也是线程共享
程序可见度:
堆不仅线程共享,整个程序也共享
栈只对线程共享,是线程私有,声明周期和线程相同
队列和栈
这里讲的是数据存储,也就是数据结构中
叫法不同,栈是进栈出栈,队列是入队,出队
队列是插入在队尾,取数据在队头,栈是存取数据都在栈头,也就是队列是先进先出,栈是先进后出
对象内存分配

分配内存时并发

内存泄漏
长生命周期的对象持有短生命周期对象的引用可能引发内存泄漏,短生命周期对象已经不需要了,但是因为长生命周期对象持有对他的引用,导致不能被回收
java回收机制:
在java中不需要显式的去释放一个对象的内存,由JVM执行,有一个垃圾回收线程,低优先级,在JVM空闲和堆内存不足时,触发执行,回收那些没有被引用的实例对象,添加到回收集合中,进行回收
GC:
当创建对象时,GC开始监控对象的地址、引用和大小,GC确定一些对象不可达(可达性算法)时,会回收这些对象的内存空间,调用system.gc()通知gc运行,但不一定立马回收
引用类型
强引用:gc也不会回收
软引用:有用但不是必须存在的对象,在内存溢出之前会被回收
弱引用:有用但不是必须存在的对象,在下一次GC时会被回收
虚引用:不能通过虚引用获取对象,要通过PhantomReference实现虚引用,用途是在gc时会返回一个通知
判断对象是否可以被回收:
引用计数器,每个对象都有一个引用计数器,被引用一次+1,当前引用被释放就-1,当引用计数器=0时,可以回收,不能解决循环引用
可达性算法,从gc roots向下检索,搜索走过的路径称为引用链,当对象对应gc roots引用链上没有任何引用时,可以回收
jvm回收算法:
标记-清除法:标记无用对象,进行清除---效率不高,无法清除垃圾碎片
复制算法:按照容量一分为二,当一份用完时,将或者的对象复制到另一份,把已使用的内存情理
标记-整理:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界意外的内存
分代算法,根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代使用复制算法,老年代使用标记整理算法
类加载器:
启动类加载器 BootStrap ClassLoader 加载java核心类库,无法被java程序直接使用
扩展类加载器 extensions class loader 加载java扩展库
系统类加载器 system class loader 根据java应用的类路径加载类
用户自定义类加载器,继承ClassLoader类实现
类加载的过程:
加载,根据查找路径查找相应的class文件导入JVM的运行时区域
验证:检查加载的class文件的正确性
准备:给类中的静态变量分配内存
解析:jvm将常量池中的符号引用替换成直接应用的过程,变量类似变成对象类型
初始化:对静态变量和静态代码块进行初始化
使用、
卸载
类的加载:
将类的.class文件中的二进制数据读入到内存(运行时数据区)中,将数据放在方法区中,然后在堆区创建一个java.lang.Class对象,用来封装在方法区内的数据结构
双亲委派:任何一个类加载器收到了加载类的请求,不会自己直接加载类,而是将这个请求交给父类,如果父类能加载,就由父类加载,如果父类加载器不能加载,反馈给子类,由子类加载器加载这个类。每一层的类加载器都是这样,所以所有的加载请求都会被传递到定层的bootstrap classloader中,只有他不能加载时,才反馈给子类加载
使用Profiler和Heap dump来查看java堆空间,检查对象内存,和内存泄漏
ClassLoader源码
自定义类加载器
继承ClassLoader
重写findClass方法
重要的是二进制数据的解析
不要重写loadClass方法,破坏双亲委托