jvm讲解-jvm内存结构详解

jdk1.8以前的jvm的内存结构图(有方法区的概念):

以上这个图可以看出内存结构的构成:

  1. 方法区
  2. 虚拟机栈
  3. 程序计数器
  4. 本地方法栈
  5. 直接内存

下面开始详细介绍这些内容;

jdk1.8以后的jvm的内存结构图(引入了元空间概念),下面有具体的讲解。

1. 方法区(线程共享)

1.1 定义:

  被所有线程共享的一块内存区域。

  用于存储已被虚拟机加载的类信息,常量,静态变量等。

  这个区域的内存回收目标主要针对常量池的回收和对类型的卸载。

  当方法区无法满足内存分配需求时,则抛出OutOfMemoryError异常。

  JDK1.7中,已经把放在永久代的字符串常量池移到堆中。

  JDK1.8撤销永久代,引入元空间。

1.2 组成

jdk 1.8 后有元空间的概念,方法区不再占用jvm内存,占用本地内存。

1.3 方法区内存溢出

 1.8 以前会导致永久代内存溢出

永久代内存溢出 java.lang.OutOfMemoryError: PermGen space

修改永久代内存命令 -XX:MaxPermSize=8m

1.8 之后会导致元空间内存溢出

元空间内存溢出 java.lang.OutOfMemoryError: Metaspace

修改元空间内存命令 -XX:MaxMetaspaceSize=8m

场景:Spring,Mybatis 底层都用到了动态代理,动态生成类的字节码。

1.4 运行时常量池

常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量 等信息

运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址(字符串在运行时,用到了才会存到堆中的StringTable中--相当于懒加载,串池中值是唯一的)

反编译class 命令:Javap –v HelloWord.class

1.5 练习了解StringTable

先看几道题:

String s1 = "a";

String s2 = "b";

String s3 = "a" + "b";  //javac 在编译期间的优化(常量不可能再变),结果已经在编译期确定为ab

s4 //new StringBuilder.append(“a”).append(“b”).toString()   new String(“ab”)

s4 //是新对象,在堆中。常量在常量池中

String s4 = s1 + s2;

String s5 = "ab";

String s6 = s4.intern();

问题如下:

System.out.println(s3==s4);//false

System.out.println(s3==s5);//ture 

System.out.println(s3==s6);//ture

String x2 = new String("c") + new String("d");

String x1 = "cd";

x2.intern();

// 问题如下:

System.out.println(x1 == x2);  //jdk1.6 //false

System.out.println(x1 == x2);  //jdk1.8 //ture 

1.6 StringTable 特性

1.常量池中的字符串仅是符号,第一次用到时才变为对象

2.利用串池的机制,来避免重复创建字符串对象

3.字符串变量拼接的原理是 StringBuilder 1.8

4.字符串常量拼接的原理是编译期优化

5.可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池

        jdk1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,会把串池中的对象返回

        jdk1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池, 会把串池中的对象返回

1.7 StringTable 位置

StringTable本质也就是常量池,以前存在于方法区中,jdk1.7以后就进行了修改。放到了堆中,因为字符串比较多,垃圾回收能够及时清理多余的数据。

1.8 StringTable 垃圾回收

-XX:+PrintStringTableStatistics查看字符串参数的大小信息

 

-XX:+PrintGCDetails -verbose:gc 垃圾回收的详细信息(次数,时间。。。)

1.9 StringTable 性能调优

调整 -XX:StringTableSize=桶个数,桶数越多会越快(减少了比对的情节)

考虑将字符串对象是否入池(string.intern())

2. 

2.1 定义:

Heap

通过 new 关键字,创建对象都会使用堆内存

特点:1.它是线程共享的,堆中对象都需要考虑线程安全的题

           2.有垃圾回收机制

2.2 堆内存溢出

堆内存溢出报错:java.lang.OutOfMemoryError

堆内存默认大小是4g,修改堆内存大小命令:-Xmx256m

2.3 堆内存诊断

1. jps 工具

查看当前系统中有哪些 java 进程

2. jmap 工具

查看堆内存占用情况命令:jmap - heap 进程id

 3. jconsole 工具

图形界面的,多功能的监测工具,可以连续监测

案例

垃圾回收后,内存占用仍然很高,定位问题:jvisualvm

3. 虚拟机栈

3.1 定义

Java Virtual Machine Stacks Java 虚拟机栈)

    每个线程运行时所需要的内存,称为虚拟机栈

    每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存

    每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

问题辨析

  1. 垃圾回收是否涉及栈内存?  不涉及,运行后会自动弹出
  2. 栈内存分配越大越好吗?   不是,内存一定,栈内存分配的大,线程数就会变少
  3. 方法内的局部变量是否线程安全?  普通变量是安全的。静态变量是共享的不安全。

         如果方法内局部变量没有逃离方法的作用访问,它是线程安全的

         如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

3.2 栈内存溢出

栈帧过多导致栈内存出(递归)

栈帧过大导致栈内存出--基本上不会出现

栈内存默认大小是1M,修改栈内存大小 -Xss256k

3.3 线程运行诊断

案例1cpu 占用过多

定位:1.用top定位哪个进程对cpu的占用过高

           2.ps H -eo pid,tid,%cpu | grep 进程id (用ps命令进一步定位是哪个线程引起的cpu占用过高,线程需要转成16进制配合jstack)

           3.jstack 进程id可以根据线程id 找到有问题的线程,进一步定位到问题代码的源码行号

4.程序计数器

4.1 定义

Program Counter Register 程序计数器(寄存器)

作用,是记住下一条jvm指令的执行地址(有时间片的概念,在一定时间内还没运行完就会暂时释放线程)

特点:1.是线程私有的  2.不会存在内存溢

4.2 作用

0:

getstatic

#20

// PrintStream out = System.out;

3:

astore_1

// --

4:

aload_1

// out.println(1);

5:

iconst_1

// --

6:

invokevirtual #26

// --

9:

aload_1

// out.println(2);

10:

iconst_2

// --

11:

invokevirtual #26

// --

14:

aload_1

// out.println(3);

15:

iconst_3

// --

16:

invokevirtual #26

// --

19:

aload_1

// out.println(4);

20:

iconst_4

// --

21:

invokevirtual #26

// --

24:

aload_1

// out.println(5);

25:

iconst_5

// --

26:

invokevirtual #26

// --

29:

return

5.本地方法栈

本地方法:不是用Java语言编写的方法,因为Java语言是有一定限制的,有些情况下它是不能直接和操作系统打交道。这时就需要调用一些用C或C++编写的方法去跟操作系统底层打交道。Java语言可以调用这些本地方法间接的调用操作系统底层的一些功能。

本地方法栈:本地方法运行时所需要的内存就是本地方法栈。

6.直接内存

6.1 定义

 直接内存Direct Memory

常见于 NIO 操作时,用于数据缓冲区

分配回收成本较高,但读写性能高

不受 JVM 内存回收管理 System.gc();

6.2 分配和回收原理

使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法

ByteBuffer 的实现类内部,使用了 Cleaner (虚引用)来监测 ByteBuffer 对象,一旦 ByteBuffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner clean 方法调 freeMemory 来释放直接内存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值