JVM内存区域
程序计数器
当前线程执行字节码文件的行号指示器,各线程独立互不干涉。线程私有。(不会内存溢出)
栈
每个方法在执行时都会创建一个栈帧。用来存储数据和部分过程结果的数据结构。存储局部变量表,操作数栈,动态链接,方法出口等信息。方法结束销毁
本地方法栈
native方法的信息。无需创建栈帧,直接动态链接到这个区域的对应方法,直接调用native方法。
堆
创建的对象和数组保存在这里。垃圾回收的重要内存区域。从gc的角度还可以分为(Eden 区、From Survivor 区和 To Survivor 区)和老年代)//栈上分配的对象存在栈中
方法区(永久代、元空间)
被jvm加载的类信息,常亮,静态变量,即时编译后的代码等数据。
不会在运行期间堆永久代进行gc,随意可能会因为class太多而oom
1.8以后没有了永久带。改成了元空间。最大的区别在于,元空间不在虚拟机中,使用本地内存。
运行时常量池
是方法区的一部分。编译期的各种字面量和符号引用。(1.8之后属于堆的一部分)
从gc的角度,把堆分成新生代(minor gc),老年代(major gc),永久代(full gc)
新生代
eden区(对象诞生于此),server from区。server to区。eden不够用会触发minorGC(复制算法:复制-清除-置换)
大对象直接进入老年代,复制的时候空间不够直接进入老年代
老年代
满了会进行majorGC(标记清除算法)1.耗时长2.产生内存碎片
栈上分配
栈上分配:开启栈上分配可以提高性能,同时要开启逃逸分析和标量替换。
对象不会分配到堆中,而是直接分散存储在栈中。
虚拟机中的对象
new一个对象时基本上是这5步:
1.执行对应的类加载过程
2.在堆中划分一块区域给对象(可能会采用指针碰撞,或者空闲列表两种方式,这取决于堆是否规整。而是否规整取决于gc算法)
这里还要注意线程安全的问题:可以预分配一块区域给不同的线程(本地线程分配缓冲)还有一种方式是同步处理分配内存空间的动作,使用cas的方式。
3.内存分配好后,虚拟机初始化内存空间,赋初值
4.包装对象头。哪个类的实例,如何能找到元数据,对象的哈希,gc的年代年龄等,包装到对象头。
5.创建完成,程序员拿到了对象,可以开始初始化对象了。
堆参数设置和内存溢出
堆溢出:分配了巨型对象,或者循环大量分配对象。动态生成大量class。或动态生成大量jsp等。
栈溢出:递归,超过了最大深度;申请扩容时没有空间了。
本地直接内存溢出:本地内存不够用了。(元数据太多了)