我的java笔记之自动内存管理机制
1.java内存区域和内存溢出异常
2.1运行时数据区域
Java虚拟机在执行java程序会把他所管理的内存划分为若干不同的数据区域,这些区域都有自己各自的用途,以及创建和销毁的时间,有的区域随着虚拟机启动而存在,有的区域则依赖用户线程的启动和结束而建立和销毁,java虚拟机所管理的内存包括以下几个方面
1.程序计数器
程序计数器可以看成是当前线程的所执行字节码的行号指示器。在虚拟机字节码解释器工作就是通过改变这个计数器的值来选择下一条需要执行的字节码命令,分支循环,调转异常处理,线程恢复等基础功能都需要依赖他来实现。程序计数器属于线程私有的。
如果线程正在执行的是一个native方法,则这个计数器为空。此内存区域是唯一一个没有规定OutOfMemoryError情况的区域
2.Java虚拟机栈
属于线程私有,他的生命周期与线程相同。虚拟机栈描述的是java方法执行的内存模型;每个方法在执行的时候都会创建一个栈帧用于存储局部变量,操作数栈,动态链接,方法出口等信息。局部变量表存放了编译时期可知的各种基本数据类型boolean byte char short int fioat long double,对象引用,他不等同于对象本身,可能是一个对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他对象的相关位置和类型returnAddress 64位的long double 占用看两个局部变量,局部变量所需的内存空间在编译时期就完成分配,当进入一个方法时,这个方法在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量的大小java虚拟机规定两种异常情况
1如果线程请求栈的深度大于虚拟机所允许的深度那么会抛出StackOverfiowerError异常
2如果虚拟机可以动态拓展,如果拓展时无法申请到内存空间那么会抛出OutOfMemoryError异常。例如递归不可以深度过深
3.本地方法栈
与虚拟机栈基本相似不过本地方栈提供的是native本地方法本地方法也会抛出StackOverfiowerError异常OutOfMemoryError异常
4.Java堆
是java内存最大的一块,所以是垃圾回收器的重点回收对象,java堆是被所有线程共享的一快区域,此内存唯一的目的就是存放对象,所有的对象实例和数组都要在堆上分配。
5.方法区
和堆一样是各个线程共享的一块内存区域,他用于存储已经被虚拟机加载过的类信息,常量,静态变量,即时编译的内存区域。当方法区无法满足内存分配需求时将抛出OutOfMemoryError异常
6.运行时常量池
是方法区域的一部分。Class文件除了有类的版本,字段,方法,接口,等还有一项就是常量池,用于存放编译时期生成的各种字面量和符号引用,这部分将在类加载后进入方法区的运行时常量池中存放,当常量池无法在申请到内存会OutOfMemoryError异常
7.直接内存
为了避免java堆和本地来回复制数据。类似于缓冲区也会出现OutOfMemoryError
2.2对象的创建,理解
几种OutOfMemoryError异常导致
Java堆溢出
package it.cast.net10;
import java.util.ArrayList;
import java.util.List;
public class TcpServer {
static class OOMObject{}
public static void main(String[] args) {
List
while(true){
list.add(new OOMObject());
}
}
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at it.cast.net10.TcpServer.main(TcpServer.java:12)
本地方法栈,Java虚拟机栈
实例
package cn.itcast.javase;
public class JavaVMStackSOF {
private int stackLength=1;
public void stackleak(){//递归
stackLength++;
stackleak();
}
public static void main(String[] args) {
JavaVMStackSOF oom=new JavaVMStackSOF();
try{
oom.stackleak();
}catch(Throwable e){
System.out.println("stack length"+oom.stackLength);
throw e;
}
}
}
stack length19217Exception in thread "main"
java.lang.StackOverflowError
at cn.itcast.javase.JavaVMStackSOF.stackleak(JavaVMStackSOF.java:7)
at cn.itcast.javase.JavaVMStackSOF.stackleak(JavaVMStackSOF.java:7)
实验表明在单个线程下无论由于栈帧太大还是虚拟机栈容量太小,当内存无法分配时都会出现StackOverflowError异常
建立多线程导致内存溢出,在不能减少线程数量或者更换64为虚拟机,就只能改变通过减少最大堆和减少栈容量来获得更多的线程
package cn.itcast.javase;
public class JavaVMStackOOM {
private void dontstop(){
while (true){
}
}
public void stackleakByThread(){
while(true){
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
dontstop();
}
});
thread.start();
}
}
public static void main(String[] args) {
JavaVMStackOOM joo=new JavaVMStackOOM();
joo.stackleakByThread();
}
}
出现StackOverflowError异常
方法区和运行时常量池溢出
package cn.itcast.javase;
import java.util.ArrayList;
import java.util.List;
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
List
int i=0;
while(true){
list.add(String.valueOf(i++).intern());
}
}
}
出现StackOverflowError异常