- jvm内存模型
- GC
- 类加载机制
- 内存溢出和内存泄漏
- 调优??
jvm内存模型
- 程序计数器
当前线程所执行字节码指示器 如果执行一个方法则计数器指向字节码指令地址 如果指向一个native方法则指向null。 不抛异常 - java虚拟机栈
存放局部变量,方法执行时放栈帧,当进入一个方法时需要在栈帧存放多大内存空间是知道的。
java虚拟机规范中会抛出两个异常StackOverflowError 线程请求的栈深度大于java虚拟机允许的。 当然也可以动态扩展也可以固定这块区域内存,如果动态扩展
在拓展时无法满足则抛出OutOfMemory. - 本地方法栈
调用native方法时用的。 也有两个同2一样的异常。
以上都是线程私有的。 - java堆
存放对象实例,GC回收就是回收这块的 - 方法区
存储已被虚拟机加载的类信息,常量静态常量,代码等数据 里面有运行时常量池 当常量池无法再申请到内存时 会抛出OutOfMemoryError异常。运行时常量池有个特性就是不一定要在编译时
可以加入常量池,运行时也可以 比如String的intern()方法;动态扩展常量池更省内存一些但是会增加运行时编码 JDK1.7后把常量池移到方法区了,直接导致地址一样即==与equals有时也等价 - 举例 对象的创建 A a=new A();
加载Student.class 文件进内存
在栈内存为s开辟空间
在堆内存为学术对象开辟空间
学生对象的成员变量进行显示初始化
通过构造方法对学生对象变量赋值
学生对象初始完毕,把对象地址赋值给s变量
GC
- 确定对象已死
引用计数,可达性分析:
可达性分析中可以当做GCroot的有 本地方法stack,stack区对象,方法区的一些静态常量啥的对象。 - 垃圾回收算法
复制算法 , 新生代在使用 两个区域,把一个区域里的可用对象复制到另一个,再清除这个区域所有的。
标记算法, 根据可达性分析算法,标记可达的,没被标记的清除。
新生代,老年代,持久代:
清理内存,GC的时候,新生代主要是复制算法,会把存活的对象放入到另外一个Survivor区域中,然后再把这个Eden区域和Survivor区域清除。
那么对于老年代,采用的是标记整理法,首先标记出存活的对象,然后再移动到一端。这样也有利于减少内存碎片。
大对象直接进入老年代 长期存活的对象进入老年代
判断动态对象年龄 在Survivor中相同年龄所有对象大小总和大于Survivor空间的一半 - 垃圾收集器
Serial: 单线程收集器
Parallel Scavenge: 使用复制算法收集器,也是一个并行的多线程收集器
GC日志一次停顿表示 第一个->表示总的 第二个->表示java堆使用前使用后的情况
类加载机制
双亲委派模型:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,
只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
- 启动类加载器:Bootstrap ClassLoader,跟上面相同。主要加载JVM自身工作需要的类:将%JAVA_HOME%\lib路径下或-Xbootclasspath参数指定路径下的、能被虚拟机识别的类库(仅按照文件名识别,如:rt.jar,名字不符合的类库不会被加载)加载至虚拟机内存中。启动类加载器是无法被 Java 程序直接引用的。
- 扩展类加载器:Extension ClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载JDK\jre\lib\ext目录中,或者由 java.ext.dirs 系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。
- 应用程序类加载器:Application ClassLoader,该类加载器由 sun.misc.Launcher$AppClassLoader 来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
五个阶段:
加载、验证、准备、解析、初始化
加载只是加载到内存。
验证的目的是为了确保 Class 文件中的字节流包含的信息符合当前虚拟机的要求,而且不会危害虚拟机自身的安全。
准备 只是默认初始化, int a=3 在准备阶段是 a=0;
初始化 真正赋值,调用构造函数
解析动作主要针对类或接口、字段、类方法、接口方法,解析时机 在类被加载器加载时就对常量池中的符号引用进行解析(初始化之前),一个符号引用将要被使用前才去解析它(初始化之后)。对字段进行解析时,会先在本类中查找是否包含有简单名称和字段描述符都与目标相匹配的字段,如果有,则查找结束;如果没有,则会按照继承关系从上往下递归搜索该类所实现的各个接口和它们的父接口,还没有,则按照继承关系从上往下递归搜索其父类,直至查找结束。
内存溢出和内存泄漏
内存溢出:通俗理解就是内存不够,程序所需要的内存远远超出了你虚拟机分配的内存大小,就叫内存溢出 内存泄露:内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。
内存泄漏举例:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露
jvm堆(持久区)内存溢出举例:年老代溢出原因有 循环上万次的字符串处理、创建上千万个对象、在一段代码内申请上百M甚至上G的内存 持久代溢出原因动态加载了大量Java类而导致溢出,以及生产大量的常量。 当两个地址在使用时会造成内存溢出
调优