JVM

JVM内存模型.png

优点:

  • 一次编译到处运行
  • 自动垃圾回收机制
  • 自动数组下边越界检测

程序计数器

作用:

记录JVM指令执行内存地址

特点:

  • 线程私有
  • 不存在内存溢出问题

虚拟机栈

虚拟机栈.png

特点:

  • 为线程运行需要的内存空间
  • 一个栈由一个或多个栈帧组成,栈帧的执行顺序为先入后出(对应java代码方法执行情况,方法1调用方法2 结果为方法2后入栈但是先执行完毕)
  • 栈帧为方法运行所需的内存空间,包含方法参数、局部变量、返回地址
  • 每个线程执行时只能存在一个活动的栈帧(对应正在执行的方法)

进阶

  1. 垃圾回收机制不会回收栈内存,垃圾回收机制是回收堆内存,而栈内存会跟随线程的执行自动回收
  2. 栈内存并不是设置的越大越好,因为物理内存是固定的,如果栈内存设置过大,会导致程序可用线程变少
  3. 设置栈内存大小可使用 -Xss=1m linux mac 默认大小都是1m
  4. 方法内的变量是否线程安全,主要取决与变量的作用域范围,如果仅为当前方法可以使用则为线程安全
  5. 栈内存溢出(stackOverFlowError)出现原因:
    1. 栈内栈帧过多,一般发生在递归调用
    2. 栈内栈帧过大,栈帧大小超过了栈内存大小
  6. cpu占用过高问题定位:
    1. 使用 top命令查看占用cpu过高的进程
    2. 使用 ps H -eo pid,tid,%cpu | grep 进程id 找到进程中占用cpu最多的线程
    3. 使用JVM提供的工具 jstack 进程id 查看线程信息
    4. 将线程id转换为十六进制 找到对应线程信息 查看代码出现问题行数
  7. 线程死锁:
    1. 使用jstack 进程id 查看线程信息
    2. 信息最后会显示 查到一个死锁,再查看导致死锁线程的信息定位到实际代码

本地方法栈

为由C编写的代码运行提供的一个内存空间,由**Native **修饰的方法

特点

  • 堆内存是线程共享的,堆中的对象需要考虑线程安全问题
  • new 出来的对象都在堆内存中
  • 堆内存由垃圾回收机制来维护内存

进阶

  1. 内存溢出(outOfMemery)
  2. 通过 -Xmx500m 设置堆内存最大值
  3. 通过 -Xms200m设置堆内存最小值
  4. 排查内存问题:
    1. 可以使用jconsole jvm自带可视化工具
    2. 可以使用jmap -heap 进程id 获取堆内存快照
    3. 可以使用第三方工具JVisualVM、JProfiler 来实现

方法区:

特点:

  • 方法区主要用来存放 class classLoader 运行时常量池
  • 是线程共享的内存区域,意味这所有线程都可以访问方法区中的数据
  • JDK1.8后默认大小是系统物理内存大小,没有设置上限,可以使用命令 -XX:MaxMetaspaceSize设置方法区大小,并且由方法区由永久代变成本地内存中的元空间,垃圾回收变成 native memory 中来完成,但还是由gc来完成
  • 在jdk1.8之前版本方法去被设置为永久代,永久代大小默认为64M,可以通过**-XX:MaxPermSize**选项进行设置,并且1.7之前方法区的垃圾回收是FULL GC

问题:

  1. 内存溢出:
    1. 当类比较多,并且系统内存较小或者方法区内存设置太小,1.8后会报OutofMemoryError:MetaSpace元空间内存溢出,1.8之前报OutofMemoryError:PermGen space永久代内存溢出

常量池:

特点:

  • 在的jdk1.6之前常量池在永久代也就是方法区
  • jdk1.7将常量池在堆中
  • jdk1.8常量池放在元空间(方法区由永久代改为元空间,删除了永久代),但是字符串常量池还是在堆中,其他常量存放在方法区也就是元空间

StringTable(字符串常量值)

字符串常量值在JVM内部是通过hashTable 来实现的,当程序执行过程中出现一个字符串字面量,就会先去查找是否存在,如果存在直接返回引用,不存在则将新值写入StringTable

定义:
  • 常量池中的字符串仅是一种符号只有再用到是才会变成对象
  • 利用字符串持机制避免重复创建字符串对象,从而节省空间
  • 字符串变量的拼接原理使用StringBuilder.append()
  • 字符串常量拼接是在编译器优化
  • 使用intern()方法可以主动将字符串常量池中没有的对象,放入字符串常量池
调优:
  • stringTable 在jvm中是由hashTable来实现的,而hashTable底层是由数组加链表来完成,如果hashTable size 比较小而常量比较多的情况下,平均分配的每个数组元素下链表的节点也就会比较多,查找起来也就相会变慢,所以 我们可以通过命令 -XX:StringTableSize=size个数来设置Hashtable的size大小,减小分配到每个链表的节点个数
  • 当程序中存在大量的字符串时,可以考虑使用用 String 的intern()方法入串池,减少堆内存占用。

JDK JVM版本对比图

DscdOul1Kn.jpg

    评论
    0 评论
avatar

取消