文章来由

C/C++ vs java

GC的概念随着JAVA兴起逐渐被人们所熟知,

C/C++   delete/free手动管理内存

java        虚拟机自动内存管理机制

java的GC由于需要后续扫描,执行效率上下降是必然的,但是带来的好处是不会因为遗漏导致内存泄漏,不会因为多次回收导致非法访问

但正因如此,当出现内存泄漏和溢出,不了解虚拟机怎么使用内存,排查会是一项艰难的工作。

好吧,就是因为不经常使用,自己每次查这个比较费劲,整理一下方便自己。

 

明确问题发生区域

java运行时数据区域

 1,程序计数器  Program Counter Register

线程私有(独立存储,互不影响),是一块很小的内存空间,是当前线程所执行字节码的行号指示器,帮助再次获取执行权限时不至于重复执行。

 

2,Java虚拟机栈 Java Virtual Machine Stack

线程私有,是java方法执行的线程内存模型,每个方法执行时,JVM都会创建一个栈帧(Stack Frame)用于存储局部变量表,操作数栈,动态连接,方法出口等信息。一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

 

局部变量表:存放编译器的基本数据类(boolean,byte,char,short,int,float,long,double),reference类型(对象引用),returnAddress类型(指向一条字节码指令的地址)

3,本地方法栈 Native Method Stacks

与虚拟机栈相似,不同的是为虚拟机使用本地方法Native服务。

 

4,java堆  Java Heap

虚拟机管理的最大的一块内存,被所有线程共享,存放几乎所有的对象实例和数组。

逃逸分析:栈上分配(私有的小对象),标量替换,同步消除

栈上分配:由于在堆上分布的对象回收消耗了大量的资源,对于确定不会逃逸出线程之外的对象,在栈上分配,随着线程方法结束消耗,大大降低了GC压力。(支持方法逃逸,不支持线程逃逸)

 public void m1(){
        T t = new T();//可逃逸
 }


 T t;
 public void m2(){
        t = new T();//不可逃逸
 }

 

标量替换

前提是方法中不可逃逸,在执行过程使用对象中的成员变量代替对象本身使用。

class Point{
 int x;
 int y; 
 get..
 set.. 
}

Point x = new Point()
x.setX(1);
int xi = x.i;

 

同步消除:

一个变量不会逃逸出线程,这个变量读写不会有竞争,那么这个变量可以安全消除掉。

public int test(int x){
    int xx = x+2;
    int num1 = xx;
    int num2 = 8;
    return num1;
}

//无效代码消除后
public int test(int x){
    return x+2;
}

 

 

 

5,方法区 Method Area

线程共享,存储已经被虚拟机加载的类型信息,常量,静态变量,即时编译器编译后的代码缓存等数据。

永久代 Permanent Generation

HotSpot虚拟机实现的时候吧手气器的分代设计扩展至方法区,使用永久代实现方法区。使得HotSpot垃圾收集器能够像管理JAVA堆一样管理这部分内存,省去了放去去内存管理代码编写。但这种也带来了JAVA应用更容易出现内存溢出的问题。

由于后续收购了JRockit,在JDK7把原本放在永久代的字符串常量,静态变量等溢出,直至JDK8完全废弃了永久代的概念,改用与JRockit,J9一样在本地内存中实现元空间 Meta-space来代替,吧JDK7中的类型信息全部移到元空间中。

 

5.1,运行时常量池

方法区的一部分,Class文件中除了有类版本,字段,方法,接口等描述信息,还有一项信息是在常量池表,用于存放编译器生产的各种字面量与符号引用,这部分内存在类加载后存放到方法区的运行时常量池中。

 

 

 

6,直接内存

不是虚拟机运行时数据区的一部分,用于分配Native函数库,通过存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样分配的好处在于避免了JAVA堆和Nacive堆中来回复制数据。

直接内存的分配不会受到JAVA堆大小的限制,但是也会受到本机总内存(物理内存,CWAP分区或者分页文件)大小以及处理器寻址空闲的限制,如果只按照实际内存去设置-Xmx等参数信息,忽略到直接内存,使得整个内存区域综合大于物理内存限制(物理和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。

 

直接内存(Direct Memory)的容量大小可通过-XX:MaxDirectMemorySize参数来指定,如果不 去指定,则默认与Java堆最大值(由-Xmx指定)一致。

 

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐