深入理解java虚拟机阅读笔记
Jdk=java语言+jvm+api
Java技术体系分为4个平台:java card,me,se,me
Java虚拟机在java程序执行过程把jvm的内存划分位若干不同数据区域。这些区域有各自的用途,以及创建和销毁时间。有些区域随虚拟机的进程的启动而存在,有些区域依赖用户线程的启动和结束而建立和销毁。
由所有线程共享的数据区---方法区、堆
线程隔离的数据区---虚拟机栈、本地方法栈、程序计数器
程序计数器较小的内存:当前线程所执行的字节码的行号指示器。分支、循环、跳转、异常处理、线程恢复等都需要依赖选取下一条指令。
Java虚拟机的多线程:通过线程轮流切换并分配处理器执行时间的方式来实现。任一时刻,一个处理器(对于多核处理器是一个内核)只会执行一条线程中的指令。
线程切换后为了恢复正确的执行位置,每条线程都需要一个独立的程序计数器。各个线程之间的计数器互不影响,独立存储,是线程私有的内存。
若正在执行native方法,则程序计数器为空,这个区域也是唯一一个没有规定OOM的区域。
虚拟机栈:线程私有,生命周期和线程一样。
每个方法被执行的时候同时创建一个栈帧:用来存储局部变量表、操作栈、动态链接、方法出口等。方法从调用到完成,对应栈帧从入栈到出栈。
局部变量表:存放编译期可知的各种基本数据类型(编译期间完成分配-运行期不改变。64位的Long和double占2个局部变量空间slot,其他是1个)、对象引用、returnAdress类型(指向一条字节码指令的地址)
栈深度大于虚拟机所允许深度,将抛出stackoverflowerror异常。
如果虚拟机栈扩展时无法申请足够多内存会抛出outOfMemoryError异常
深入理解java虚拟机阅读笔记
Jdk=java语言+jvm+api
Java技术体系分为4个平台:java card,me,se,me
Java虚拟机在java程序执行过程把jvm的内存划分位若干不同数据区域。这些区域有各自的用途,以及创建和销毁时间。有些区域随虚拟机的进程的启动而存在,有些区域依赖用户线程的启动和结束而建立和销毁。
由所有线程共享的数据区---方法区、堆
线程隔离的数据区---虚拟机栈、本地方法栈、程序计数器
程序计数器较小的内存:当前线程所执行的字节码的行号指示器。分支、循环、跳转、异常处理、线程恢复等都需要依赖选取下一条指令。
Java虚拟机的多线程:通过线程轮流切换并分配处理器执行时间的方式来实现。任一时刻,一个处理器(对于多核处理器是一个内核)只会执行一条线程中的指令。
线程切换后为了恢复正确的执行位置,每条线程都需要一个独立的程序计数器。各个线程之间的计数器互不影响,独立存储,是线程私有的内存。
若正在执行native方法,则程序计数器为空,这个区域也是唯一一个没有规定OOM的区域。
虚拟机栈:线程私有,生命周期和线程一样。
每个方法被执行的时候同时创建一个栈帧:用来存储局部变量表、操作栈、动态链接、方法出口等。方法从调用到完成,对应栈帧从入栈到出栈。
局部变量表:存放编译期可知的各种基本数据类型(编译期间完成分配-运行期不改变。64位的Long和double占2个局部变量空间slot,其他是1个)、对象引用、returnAdress类型(指向一条字节码指令的地址)
栈深度大于虚拟机所允许深度,将抛出stackoverflowerror异常。
如果虚拟机栈扩展时无法申请足够多内存会抛出outOfMemoryError异常
本地方法栈:服务虚拟机使用的native方法。会抛出stackoverflow和outofmemoryError异常
Java堆:jvm所管理的最大一块内存。Jvm启动时创建。唯一目的:存放对象实例。物理上可以不连续,但逻辑上必须联系。
方法区:存放已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等。
Class文件:类的版本、字段、方法、接口、常量池等描述信息
运行时常量池:方法区的一部分,在类加载后存放编译器生成的各种字面量和符号引用,保存class文件中描述的符号引用,还会把翻译出来的直接引用也存储在运行时常量池。jvm堆class文件的每一部分的格式都有严格规定,但对运行时常量池没有任何规定。运行时常量池具备动态性,运行期间也可能将新的常量放入运行时常量池中。存在oomE异常.
直接内存:
直接内存:不是jvm的一部分。但也会被使用。也有oomE的异常。本块内存不受java堆限制
NIO类:基于通道和缓冲区的I/O方式使用native函数库直接分配堆外内存。然后通过java堆里的directByteBuffer对象作为这块内存的引用进行操作。
不同虚拟机实现对象的访问方式有2种:句柄和直接指针。
句柄方式:在堆种直接划分出一块内存作为句柄池。Referrence中就保存对象的句柄地址,句柄中包含对象实例数据和类型数据的具体地址。优点:refference中存储稳定的句柄地址,对象被移动只会改变句柄中的实例数据指针,refference本身不需要修改。
直接指针:refference中存储对象地址。优点:访问速度快,因为少了一次指针定位时间。Sunspot就是使用这种方式。
解决java.lang.OutOfMemoryError之后跟java heap space的异常信息:
重点确认内存中的对象是否是必要的。即确认是内存泄漏还是内存溢出。
内存泄漏:需要掌握泄露对象的类型信息和GCRoots引用链的信息,就能准确定位内存泄露代码的位置。
然后检查是否某些对象存在生命周期过长或者持有状态时间过长等情况。
32位操作系统为每个线程分配的的内存是2G
栈深度在大多数情况下达到1000-2000是没有问题的。
若建立过多线程导致内存溢出,在不能减少线程数或更换64位操作系统的情况下,只能通过减少最大堆和减少栈容量来换取更多线程。
深入理解java虚拟机阅读笔记
Java和C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙。
Jdk=java语言+jvm+api
Java技术体系分为4个平台:java card,me,se,me
Java虚拟机在java程序执行过程把jvm的内存划分位若干不同数据区域。这些区域有各自的用途,以及创建和销毁时间。有些区域随虚拟机的进程的启动而存在,有些区域依赖用户线程的启动和结束而建立和销毁。
由所有线程共享的数据区---方法区、堆
线程隔离的数据区---虚拟机栈、本地方法栈、程序计数器
程序计数器较小的内存:当前线程所执行的字节码的行号指示器。分支、循环、跳转、异常处理、线程恢复等都需要依赖选取下一条指令。
Java虚拟机的多线程:通过线程轮流切换并分配处理器执行时间的方式来实现。任一时刻,一个处理器(对于多核处理器是一个内核)只会执行一条线程中的指令。
线程切换后为了恢复正确的执行位置,每条线程都需要一个独立的程序计数器。各个线程之间的计数器互不影响,独立存储,是线程私有的内存。
若正在执行native方法,则程序计数器为空,这个区域也是唯一一个没有规定OOM的区域。
虚拟机栈:线程私有,生命周期和线程一样。
每个方法被执行的时候同时创建一个栈帧:用来存储局部变量表、操作栈、动态链接、方法出口等。方法从调用到完成,对应栈帧从入栈到出栈。
局部变量表:存放编译期可知的各种基本数据类型(编译期间完成分配-运行期不改变。64位的Long和double占2个局部变量空间slot,其他是1个)、对象引用、returnAdress类型(指向一条字节码指令的地址)
栈深度大于虚拟机所允许深度,将抛出stackoverflowerror异常。
虚拟机栈扩展时无法申请足够多内存会抛出outOfMemoryError异常
本地方法栈:服务虚拟机使用的native方法。会抛出stackoverflow和outofmemoryError异常
Java堆:jvm所管理的最大一块内存。Jvm启动时创建。唯一目的:存放对象实例。物理上可以不连续,但逻辑上必须联系。
方法区:存放已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等。
Class文件:类的版本、字段、方法、接口、常量池等描述信息
运行时常量池:方法区的一部分,在类加载后存放编译器生成的各种字面量和符号引用,保存class文件中描述的符号引用,还会把翻译出来的直接引用也存储在运行时常量池。jvm堆class文件的每一部分的格式都有严格规定,但对运行时常量池没有任何规定。运行时常量池具备动态性,运行期间也可能将新的常量放入运行时常量池中。存在oomE异常.
直接内存:
直接内存:不是jvm的一部分。但也会被使用。也有oomE的异常。本块内存不受java堆限制
NIO类:基于通道和缓冲区的I/O方式使用native函数库直接分配堆外内存。然后通过java堆里的directByteBuffer对象作为这块内存的引用进行操作。
不同虚拟机实现对象的访问方式有2种:句柄和直接指针。
句柄方式:在堆种直接划分出一块内存作为句柄池。Referrence中就保存对象的句柄地址,句柄中包含对象实例数据和类型数据的具体地址。优点:refference中存储稳定的句柄地址,对象被移动只会改变句柄中的实例数据指针,refference本身不需要修改。
直接指针:refference中存储对象地址。优点:访问速度快,因为少了一次指针定位时间。Sunspot就是使用这种方式。
解决java.lang.OutOfMemoryError之后跟java heap space的异常信息:
重点确认内存中的对象是否是必要的。即确认是内存泄漏还是内存溢出。
内存泄漏:需要掌握泄露对象的类型信息和GCRoots引用链的信息,就能准确定位内存泄露代码的位置。
然后检查是否某些对象存在生命周期过长或者持有状态时间过长等情况。
32位操作系统为每个线程分配的的内存是2G
栈深度在大多数情况下达到1000-2000是没有问题的。
若建立过多线程导致内存溢出,在不能减少线程数或更换64位操作系统的情况下,只能通过减少最大堆和减少栈容量来换取更多线程。
来源:oschina
链接:https://my.oschina.net/u/4263828/blog/4150167