Skip to content

Latest commit

 

History

History
80 lines (65 loc) · 3.89 KB

JVM.md

File metadata and controls

80 lines (65 loc) · 3.89 KB

  • String s = new String("abc"),这里创建了两个对象,"abc"存在常量池,new String("abc")存在堆上,s引用存在栈中,指向堆内存的对象

jvm内存结构

  • 堆(新生代,老年代[调用次数多,大对象])
    • 一切new出来的对象
  • 方法区(类信息,常量池,静态变量,jit编译后的代码)
  • java栈
    • 对象引用和基本数据类型
  • 本地方法栈(调用native方法,执行其他底层语言)
  • 程序计数器(线程私有,为了切换线程能正常执行)
  • 在JDK1.8版本废弃了f方法区,替代的是元空间,元空间并不在JVM中,而是使用本地内存

gc过程(分代回收策略)

  • 一般策略是分代回收,分为新生代(Eden,存活区0,存活区1,默认比例8:1:1),老年代
  • 一般是直接分配在Eden区,Eden区满后,minorGC,没gc掉的放在存活区0
  • 当存活区0满了后,将还活着的分配到存活区1
  • 以后Eden区执行minorGC,把没gc的放到存活区1
  • 存活区的数据来回切换n次,移到老年区,对应jvm参数:-XX
  • MaxTenuringThreshold控制,大于该值进入老年代
  • 标记-清除
  • 复制算法(新生代)
  • 标记-压缩(老年代)

gc算法

  • 标记清除
    • 遍历所有gc root,标记所有可达对象,将未被标记的对象清除
    • 缺点:清除后内存分散
  • 复制算法
    • 新生代使用的算法
    • 将内存分为两块,每次只使用一块,在回收时,将正在使用的存活对象复制到未使用的内存块中,之后清除正在使用的内存块所有对象,交换两个内存的角色,完成垃圾回收
    • 缺点:需额外的空闲内存
  • 标记整理
    • 老年代使用的算法
    • 和标记清除法类似,不过回收时,会对所有存活的对象按地址排序,其他的清除
    • 弥补了标记清除内存分散的特点,同时也比复制算法节约空间,但有个排序整理过程,所以比较慢

内存溢出 OOM

  • Java heap space(堆溢出)
    • 虚拟机分配的到堆内存空间已经用满
    • 是否有死循环或不必要地重复创建大量对象
    • 调整jvm参数,xms,xmx
  • PermGen space(方法区溢出)
    • 载入的class等信息超过了阀值
    • -XX:PermSize和-XX:MaxPermSize设置永久代大小即可
  • Thread Stack space(栈溢出)
    • 检查程序中递归调用是否未控制好出口导致递归死循环
    • jvm参数调整xss
  • 内存泄漏

内存泄漏

  • 无效对象不能被释放
  • 借助MAT等工具检查是否泄漏
  • 单列造成的内存泄漏
    • 如果一个对象不再需要被使用,而单例还持有该对象的引用,造成不能被正常回收
  • 各种连接
    • io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的
  • 内部类造成的内存泄漏
    • 非静态内部类会持有它们所属外部类的引用,一旦没释放可能导致一系列的后继类对象没有释放
  • 静态集合类引起内存泄漏,引用的对象不能被释放

jvm调优

  • 调优策略:不要定义太多常量,占内存。存放在方法区,不会被gc
  • 堆内存 Xms初始值和Xmx最大值设成一致 因为gc会使内存趋向于初始值,减少gc次数
  • -XX:+PrintGC 每次触发GC的时候打印相关日志
  • -Xmn 新生代堆最大可用值
  • -Xss 设置最大调用深度(解决栈溢出)
  • MaxTenuringThreshold控制,大于该值进入老年代
  • 最大堆内存最小堆内存设置为一致,因为gc会靠向最小堆内存,导致平凡的full gc
  • Perm Space(永久代),超出大小会OutOfMemory,方法区(方法区是jvm规范,永久代和元空间是实现),保存class、运行时常量池、字段、方法、代码、JIT代码等,在jdk1.7以前,在永久代,1.8元空间,在物理内存上
  • jstack(跟踪线程堆栈,执行到哪一步)jmap(看内存情况)jvm调优工具