目录
运行时数据区:在执行java程序过程中会内存分成若干区域
线程共享区
-
方法区:加载相关类的信息
-
常量池:一定是放在方法区的
-
静态常量池:即*.class文件中的常量池,包含字符串字面量,类、方法的信息,符号引用
-
方法里定义的常量
-
运行时常量池:在jvm完成类装载操作后,将class文件中的常量池加载入内存,并保存在方法区中,把符号引用变为直接引用
-
字符串常量池:为了高效使用String,所以有这种常量池
-
String不可变:不可变安全,hash唯一
-
String str = "abc":会在字符串常量池创建"abc",如果存在会返回这个字符串的引用
-
Stirng str = new String("abc"):先去字符串常量池创建"abc",然后调用new会在堆中创建String对象,并引用常量池中字符串对象char[]数组,并返回String对象的引用
-
String str = "ab" + "cd" + "ef":编译器会自动优化成"abcdef"
-
在运行时例如private str会直接在堆中生成
-
-
-
-
堆
-
堆空间分代划分:Eden、From、To、Old(老年代)
-
线程私有区(所有的线程单独用一块内存区域)
-
虚拟机栈:存储当前线程运行java方法所需的数据、指令、返回地址,一般是业务方法不是本地方法
-
栈的优化技术:两个相邻栈帧可以将操作数粘和局部变量表共享一部分区域来共享参数
-
线程运行java方法
-
iconst将数据放到操作数栈,istore_n将刚才的变量从操作数栈存储在局部变量表n的位置
-
iload_n,将局部变量表第n个位置的数据放到操作数栈
-
类似于iadd方法,将需要操作的数据出栈进行计算,结果入栈进入操作数栈顶
-
如果还需要对常量操作,bipush常量进操作数栈
-
然后循环2、3、4进行执行计算操作,知道计算公式结束,istore存储在局部变量表
-
需要return结果时候,iload_n的位置的局部数据,ireturn此结果
-
-
-Xss:代表栈的大小,可以自己设置,默认值取决于操作系统平台
-
栈溢出:栈大小受限制,并且有方法不断进入并不结束会栈溢出
-
栈帧:会生成堆中对象的引用
-
操作数栈
-
局部变量表:八种基本数据类型和对象的引用
-
动态连接
-
完成出口:记录每个栈帧完成后的返回行号
-
-
-
本地方法栈:为JVM使用到本地方法服务弄一块区域,虚拟机规范需要有这一块地址
-
程序计数器:记录当前线程字节码指令的地址,线程来回切换时候记录执行位置,并且不会发生内存溢出
直接内存或者堆外内存
除了方法区、堆、线程虚拟机栈、本地方发展、程序计数器占用的内存之外的内存,NIO中的DirectByteBuffer使用直接内存,直接内存没有虚拟化,jvm没有管理这块区域,必须自己回收这块内存
深入辨析栈和堆
- 栈主要存储基本数据类型以及它们对象的引用,线程占用内存是独有的,且各个线程的数据不能共享,线程结束占用的空间就释放
- 几乎所有对象是存储在堆中间的,类中的成员变量在new出来也在堆中间,几乎所有对象都是堆中可见,线程共享
jvm代码内存处理流程
- jvm内存申请:向操作系统
- 初始化运行时数据区:方法区和堆
- 类加载:包括jre等等的jar,都在方法区,存放class、 静态变量和常量
- 执行方法:方法运行然后方法入栈(栈帧)
- 创建对象:在堆中创建对象,对象引用在栈帧中生成
内存溢出(OutOfMemory)
栈溢出
-
死递归方法(StackOverflow)
-
很多线程在使用除堆和方法区剩余的内存
堆溢出
-
堆中的对象总和超过最大大小(heap space)
-
对象在不断添加,会进行垃圾回收,尝试很多此发现是无效的,如果垃圾回收的线程占用了98%的资源,并且回收的效率不足2%(GC overhead limit)
方法区溢出(Metaspace)
-
不断的在方法区中放入class
本机内存直接溢出
-
限制最大直接内存大小,NIO中的DirectByteBuffer大小,在使用ByteBuffer时候有两种分配大小方式,allocate是分配的jvm堆内存,allocteDirect是分配的堆外内存