JVM基础学习

JVM基础学习_第1张图片

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的运行时数据区此区域包含:

线程数据共享区:方法区,堆区

线程数隔离区:虚拟机栈,方法栈,程序计数器

  1. 程序计数器:当前程序所执行的字节码行号指示器,字节码解析的工作就是通过改变计数器的值,一行一行的解析字节码指令,分支、循环、跳转、异常处理,线程恢复等基础功能都依赖这个计数器

  1. 虚拟机栈:存储局部变量表、操作数栈、动态链接、方法出口

  1. 本地方法:与虚拟机栈作用一样,只是为了调用native方法服务

  1. 堆区:jvm内存中最大的一块,被所有线程共享,几乎所有对象的实例都在这里分配内存

  1. 方法区:用来存储已经被加载到JVM内存的类信息,常量,静态变量,即时编译后的数据

浅拷贝:内存地址是同一个,增加一个指针指向此内存地址

深拷贝:增加一个指针且申请一个新的内存地址,指针指向的是新的内存地址

浅复制:复制被指向的内存地址,如果原地址发生变化,浅复制出来的对象也会发生变化

深复制:在计算机中开辟一块新的内存地址用来存放复制的对象

堆栈区别:

物理地址:

堆的物理地址是不连续的,栈的物理地址是连续的,栈使用的是数据结构中的栈,先进后出原则,性能更快

内存分别:

堆的地址是不连续的,分配内存是在运行时确认,大小不固定,堆一般远大于栈

栈是连续的,分配内存是在编译期间,大小固定

存放的内容:

堆存放的是对象的实例和数组,更关注数据的存储

栈存放:局部变量,操作数栈,返回结果,更关注方法的执行

静态变量放在方法区,也就是线程共享,静态对象放在堆区,也是线程共享

程序可见度:

堆不仅线程共享,整个程序也共享

栈只对线程共享,是线程私有,声明周期和线程相同

队列和栈

这里讲的是数据存储,也就是数据结构中

  1. 叫法不同,栈是进栈出栈,队列是入队,出队

  1. 队列是插入在队尾,取数据在队头,栈是存取数据都在栈头,也就是队列是先进先出,栈是先进后出

对象内存分配

JVM基础学习_第2张图片

分配内存时并发

JVM基础学习_第3张图片

内存泄漏

长生命周期的对象持有短生命周期对象的引用可能引发内存泄漏,短生命周期对象已经不需要了,但是因为长生命周期对象持有对他的引用,导致不能被回收

java回收机制:

在java中不需要显式的去释放一个对象的内存,由JVM执行,有一个垃圾回收线程,低优先级,在JVM空闲和堆内存不足时,触发执行,回收那些没有被引用的实例对象,添加到回收集合中,进行回收

GC:

当创建对象时,GC开始监控对象的地址、引用和大小,GC确定一些对象不可达(可达性算法)时,会回收这些对象的内存空间,调用system.gc()通知gc运行,但不一定立马回收

引用类型

强引用:gc也不会回收

软引用:有用但不是必须存在的对象,在内存溢出之前会被回收

弱引用:有用但不是必须存在的对象,在下一次GC时会被回收

虚引用:不能通过虚引用获取对象,要通过PhantomReference实现虚引用,用途是在gc时会返回一个通知

判断对象是否可以被回收:

  1. 引用计数器,每个对象都有一个引用计数器,被引用一次+1,当前引用被释放就-1,当引用计数器=0时,可以回收,不能解决循环引用

  1. 可达性算法,从gc roots向下检索,搜索走过的路径称为引用链,当对象对应gc roots引用链上没有任何引用时,可以回收

jvm回收算法:

  1. 标记-清除法:标记无用对象,进行清除---效率不高,无法清除垃圾碎片

  1. 复制算法:按照容量一分为二,当一份用完时,将或者的对象复制到另一份,把已使用的内存情理

  1. 标记-整理:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界意外的内存

  1. 分代算法,根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代使用复制算法,老年代使用标记整理算法

类加载器:

  1. 启动类加载器 BootStrap ClassLoader 加载java核心类库,无法被java程序直接使用

  1. 扩展类加载器 extensions class loader 加载java扩展库

  1. 系统类加载器 system class loader 根据java应用的类路径加载类

  1. 用户自定义类加载器,继承ClassLoader类实现

类加载的过程:

  1. 加载,根据查找路径查找相应的class文件导入JVM的运行时区域

  1. 验证:检查加载的class文件的正确性

  1. 准备:给类中的静态变量分配内存

  1. 解析:jvm将常量池中的符号引用替换成直接应用的过程,变量类似变成对象类型

  1. 初始化:对静态变量和静态代码块进行初始化

  1. 使用、

  1. 卸载

类的加载:

将类的.class文件中的二进制数据读入到内存(运行时数据区)中,将数据放在方法区中,然后在堆区创建一个java.lang.Class对象,用来封装在方法区内的数据结构

双亲委派:任何一个类加载器收到了加载类的请求,不会自己直接加载类,而是将这个请求交给父类,如果父类能加载,就由父类加载,如果父类加载器不能加载,反馈给子类,由子类加载器加载这个类。每一层的类加载器都是这样,所以所有的加载请求都会被传递到定层的bootstrap classloader中,只有他不能加载时,才反馈给子类加载

使用Profiler和Heap dump来查看java堆空间,检查对象内存,和内存泄漏

ClassLoader源码

自定义类加载器

  1. 继承ClassLoader

  1. 重写findClass方法

  1. 重要的是二进制数据的解析

  1. 不要重写loadClass方法,破坏双亲委托

你可能感兴趣的:(学习记录,jvm,java,linux)