java虚拟机笔记

上图为jvm的主要结构,学习jvm应该有以下2个模块可以去思考

1.类加载机制

加载的过程就是将class文件转为内存中的可被jvm使用的java类型

加载》》验证》》准备》》解析》》初始化》》使用》》卸载

java在什么情况洗会初始化

new 关键字,读取或者设置静态字段,调用静态方法,通过反射进行调用,类的子类被初始化的时候,如果自己没有实例化,则会初始化,jvm启动时候带main方法那个主类

1.1加载

将class文件的二进制字节流按照jvm所需要的格式存在方法区。在java堆中实例化对象

类加载器

双亲委派机制,从底层往上BootStrap  >>  Extension >> Application

如果类加载器收到类加载的请求,首先会委派给父加载器进行加载,所以都会传送到BootStrap,当父加载器无法加载的时候才会让子加载器去加载。好处是保证了java的稳定和安全

自定义类加载器

 一般web容器会自定义类加载器。

 

 

1.2验证

文件开头是否是魔数

版本号(jdk高版本可执行低版本)

常量池的常量是否有不被支持的常量类型

。。。等等(需要了解class文件)

java语法检查(抽象类,final等)

1.3准备

在方法区对类进行分配内存和变量的初始值设置。针对类变量而不是实例变量。初始值指数据类型的零值

比如 static int value = 33;  此时应该是int类型的初始值0

1.4解析

1.5初始化

 

 

2.运行时数据区

运行时数据区由方法区,堆,java栈,本地方法栈,pc寄存器(也可以叫做程序计数器),如图所示,方法区和堆是所有线程共享的。

2.1pc寄存器

pc寄存器的作用就是记录一个线程当前所执行的字节码的行号。因为线程在上下文切换的时候要继续原来的步骤,所以每个线程都会有自己的pc寄存器(就是程序计数器,这个更好理解)。

2.2java栈

java栈也是线程私有的,生命周期与线程相同。Java 栈由栈帧组成,一个帧对应一个方法调用。调用方法时压入栈帧,方法返回时弹出栈帧并抛弃。Java栈的主要任务是存储方法参数、局部变量、中间运算结果, 并且提供部分其它模块工作需要的数据。每个栈桢分配多少内存在类结构确定下来的时候就已经确定

2.3本地方法栈

省略

2.4java堆(新生代,老年代)

在虚拟机启动的时候创建,存储对象的实例以及数组

对象一般会优先在Eden区,Eden区空间不足的时候进行一次Minor GC(针对年轻代进行的垃圾回收)

大对象会直接在old区,一般指需要大量连续内存空间的对象,比如长字符串和数组。old区会进行full GC 即Major GC 一般会导致Minor GC

在新生代中,存活时间超过  Minor GC15次以上的会进入老生代。

还有就是在Survivor中年龄相同的对象总和 待遇Survivor空间一半以上的,年龄大于或者等于这些对象的对象,都可以进入老年代,无需等等15次

2.5方法区(持久代)

存放

a.类及其父类的全限定名(java.lang.Object没有父类)

b.类的类型(Class or Interface)

c.访问修饰符(public, abstract, final)

d.实现的接口的全限定名的列表

e.常量池    //这个比其他的特别

f.字段信息

g.方法信息

h.静态变量

i.ClassLoader引用

j.Class引用

一般垃圾回收针对常量池的卸载和对类型的卸载(即没什么用的类)

对常量的回收,可以理解为下文的GC Roots,即没有任何引用链,可以被回收。

没用的类是指同时满足下面条件

  • 该类的实例都已经被回收(堆中不存在该类的对象)
  • 加载这个类的classloader已经被回收
  • 该类对应的java.lang.class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法//TODO 这三个条件的理解

因为方法区的回收能够回收的很少,所以会被叫做持久代

 

2.6判断哪些对象可以被回收

2.6.1引用计数算法

给一个对象添加一个引用计数器,其他地方有1个引用就+1,最终  如果一个对象的计数器为0,则可以被回收。不过这个存在循环计数的情况,就是两个对象相互引用,所以sun并没有采取这个算法。

//TODO 什么是引用?强引用/弱引用/软引用/虚引用   发现随着经验的增加,对一些名词的理解会有不同

2.6.2根搜索算法

把这些对象作为GC Roots

  • java栈中引用的对象
  • 方法区中的类的静态属性引用的对象
  • 方法区中的常量引用的对象
  • 本地方法栈中引用的对象

通过GC Roots作为起点,向下搜索,走过的路径称为引用链,如果一个对象跟GC Roots没有引用链,则可以被回收

//TODO不过就算一个对象没有引用链,也不是一定会被回收,还有两次标记过程,finalize()方法

2.7垃圾回收的算法

2.7.1标记清除算法

上面讲过的标记为可回收的对象,然后进行清楚,1效率慢2产生大量内存碎片

2.7.2copy算法

刚开始人们会把堆的新生代分为两个相同大小的内存,垃圾回收的时候将一个内存块所有存活的对象copy到另一块。

不过后来发现绝大部分内存对象都是朝生夕死的,所以进行了优化

如图所示,将堆的新生代分为3部分。一般使用Eden和s中的1个,进行垃圾回收的时候,将存活的部分copy到s中的另一个。然后清理刚才的s和Eden.

同时注意,如果此时的s空间不足,可以去老年代进行分担。同时一些存活时间比较久的也会被copy到老年代。

2.7.3标记整理算法

这个是为老年代准备的算法,先标记,然后将存活的对象都像内存区间的一端移动,然后清理掉整理后的那端//TODO  具体算法不清楚

2.7.4分代收集算法

其实就是分为老年代和新生代

2.7.4具体的实现

7个回收器,以后可以具体看。

 

 

 

 

 

 

 

 

3.其他

3.1java对象访问

一般会有两种

3.2内存溢出

堆内存溢出

一般可以把dump出来的文件进行分析(Eclipse Memory Analyzer),查看泄漏对象到GC Roots的引用链。内存泄漏和内存溢出区别??

java栈内存溢出

一般在java栈总量一定的时候,每个线程的栈容量越大,越容易导致内存溢出。一般栈深度都是够用的。

3.3内存分析一般的工具

  1. jps  显示所有的jvm 进程
  2. jstat 收集jvm各方面的运行数据
  3. jinfo 显示jvm的配置信息
  4. jmap 生成jvm的内存快照文件(heapdump文件)
  5. jhat 可以分析heapdump文件
  6. jstack 显示虚拟机的线程的快照
  7. jconsole 可视化的
  8. VisualVM 高级工具,需要再下载

 

参考:http://www.cnblogs.com/floerggyy/archive/2008/04/01/1133353.html

 

 

posted @ 2014-03-23 23:17  新密牛哥哥  阅读(664)  评论(0编辑  收藏  举报