简单了解一下,java 运行环境的基础是JVM,android的运行环境有dalvik和art。
- jvm
- delvik
- art
- 之间的区别和联系
jvm运行时数据区
程序计数器: 线程私有的,记录程序正在执行的虚拟机字节码指令的地址。理解为记录程序执行的位置。可以确保线程切换后能恢复到正确的执行位置。(单核cpu实现多线程是由快速切换线程执行来实现多线程同步执行的)
虚拟机栈: 线程私有的,描述java方法执行的内存模型,每个方法在执行时都会创建一个栈帧用于存储局部变量表、操作数帧、动态链接、方法出口等信息。每一个方法从调用只结束就对应着一个栈帧从入栈到出栈的过程。局部变量表存放编译期可知的各种基本数据类型,对象引用和returnAddress(指向一条字节码指令地址)
本地方法栈: 线程私有的,区别于虚拟机栈是为执行java方法的,本地方法栈是为执行native方法服务的。
方法区: 线程共享,存储已被加载的类信息,常量,静态变量,及时编译器编译后的代码数据。回收目标主要是常量池回收,类卸载。 内存划分为永久代
堆 线程共享,主要是存放对象实例和数组。java程序最主要的内存工作区域。内存区域又划分为 新生代+老年代 ,新生代又划分为 eden + survivor1+survivor2
gc 垃圾回收
垃圾收集算法
- 引用计数法:为对象添加一个计数器,每当一个地方引用该对象时计数器+1,当引用失效时-1,当计数为0时认为该对象不再被引用,可以进行回收。此算法缺陷是无法解决对象循环引用问题。主流虚拟机都没有选用此算法。
- 标记清除法:利用根搜索算法对存活的对象进行标记,标记完毕后扫描未被标记的对象进行清除(回收)。此算法的缺点是容易造成内存碎片化严重。
- 复制算法:为了解决标记清除算法的缺陷,复制算法被提出来,将内存区域分为两块,只使用其中一块。当一块区域不足时,对本块内的可回收单位进行回收,并将存活的对象复制到另外一块区域中。这里有个明显的缺点就是对内存的大小要求比较高,因为只能使用1/2。
- 标记整理算法:为了解决复制算法的问题,该算法标记阶段和标记清除算法一样(利用根搜索算法进行标记存活对象)但是在清除时清除掉未被标记的对象的时候,将存活的对象进行向左移动。既解决了标记清除的碎片化问题,也解决了复制算法的空间浪费问题。
分代收集算法
- 新生代:年轻代主要以复制算法为主,年轻代对象生命周期短,回收频率比较高,所以采用较快的算法进行回收。
新生代分为3个区域: eden+suvivor0+suvivor1 。大部分对象生成时是在eden区域(特殊情况:大对象数据–需要大量连续的内存空间,会直接进入老年代)。1)eden区域不足时将存活对象复制到suvivor0区域;2)eden区域再次不足时,将eden区域和suvivor0区域存活对象复制到suvivor1区域;3)重复多次1-2步骤后会将符合条件的对象复制到老年代。 - 老年代:老年代回收算法主要以标记整理算法。
- 持久代:永久代主要存放静态文件:java类和方法等。方法区回收主要是废弃常量和回收无用的类。对于无用的类判断需要满足3个条件:1)该类所有的实例已经被回收,堆中不存在该类的任何实例;2)加载该类的classloader已经被回收;3)该类的class对象没有在任何地方被引用。
分代收集器
Serial 串行收集器
ParNew 并行收集器
CMS 收集器
G1 收集器(分区收集器):G1将整个java堆划分为多个region区域,每个区域都可能是 年轻代和老年代。
类加载
JVM会严格按照这个过程来完成类的加载
触发类加载条件
- new getstatic putstatic invokestatic四条指令触发时类没有被加载,就立即触发类的初始化;
1)new 指令就是代码中新创建一个对象
2)getstatic putstatic 指令是操作类的静态属性
3)invokestatic 指令对应的是操作类的静态方法 - 使用 java.lang.reflect包的方法对类进行反射调用时,类没有初始化,触发类的初始化;
- 初始化一个类时其父类没有初始化,会先对父类进行初始化
- 运行一个jvm必须制定一个包含main方法的类,会首先对其进行初始化;
- 使用jdk1.7动态语言支持时,MethodHandle 解析结果为REF_getstatic REF_putstatic REF_invokestatic时类没有初始化时,立即触发初始化
类加载过程
总的加载过程为:加载>>验证>>准备>>解析>>初始化>>使用>>卸载
- 加载:
1)通过类的全限定名获取类文件字节流。获取方式有jar、war、网络获取、jsp文件生成。
2)将二进制字节流静态类文件转化为运行时数据结构,这里只做数据结构转化,并未合并数据
3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口,这个比较特殊,虽然为对象,但是存在在方法区。 - 验证:
验证被加载的类是否有正确的结构,是否符合虚拟机对类的要求。确保虚拟机安全。 - 准备:为类变量分配内存并给默认值(初始化阶段才会给赋值具体值)
- 解析:将符号引用转换为直接引用
- 初始化:
如果类中有静态块先执行类中静态块代码,然后执行java类中构造器的方法,并给类变量进行赋值。 - 使用
- 卸载
类加载器(双亲委派模型)
双亲委派:如果一个类加载器收到加载请求不会立即自己执行类的加载,而是把这个加载请求委派到父的类加载器,每一层加载器都是如此,所以所有的类加载请求都会委派到bootstrapclassloader 只有父类加载器反馈自己无法加载这个请求,子类加载器才会尝试对类执行加载。注意这里并不是直接的继承关系,只是一种逻辑关系。
bootstrap classloader 加载 lib 下或被 -Xbootclasspath 路径下的类
extension classloader加载lib/ext/下类文件
application classloader 加载用户目录下制定的文件
优势:
1)安全:防止恶意修改java核心类库(java.lang.*)类文件;
2)避免重复加载,当父加载器加载过后子加载器就不用再加载了。