logo头像

Always believe youself.

JVM-1

什么是 JVM

java virtual Machine - java 程序的运行环境(java 二进制字节码的运行环境)

好处;

  • 一次编写,到处运行
  • 自动化内存管理,垃圾回收功能
  • 数组下标越界检查
  • 多态:使用虚方法调用的机制实现了多态

比较:

  • JVM
  • JRE
  • JDK

image

常见的 JVM

JVM 是一套规范

此课程参照 HotSpot

学习线路

fm6jje.md.jpg

内存结构

  • 程序计数器
  • 虚拟栈
  • 本地方法区
  • 方法区

程序计数器 program Counter Register 程序计数器(物理体现:寄存器)

  • 作用: 记住下一个jvm 指令的执行地址

fmWNQK.md.png

  • 特点
    • 线程私有的(时间片的概念 (不断的切换 线程进行运行指令),每个线程都有自己的程序计数器)
    • 不会有内存溢出(规范规定)

虚拟机栈 (线程时运行时的内存空间)

压栈, 先进后出
栈帧, 一个栈帧代表一次方法的调用,每个方法需要的内存。包含 参数,局部变量,返回地址。
运行一个方法就把 对应的栈帧放入到栈中,运行结束后拿出,如果关联其他方法,则其他方法栈帧继续压入(如递归)
  • 每个线程运行时所需的内存,称为 虚拟机栈
  • 每个栈 由 多个栈针(Frame) 组成,对应着每次方法调用时所占用的内存
  • 每个线程只能由一个活动栈帧,对应着当前正在执行的那个方法。

问题辨析

  1. 垃圾回收是否涉及栈内存?

不需要,栈帧内存,在每次方法处理完之后,会弹出栈,会自动回收,不需要垃圾回收。(垃圾回收只会回收堆内存中的无用对象)

  1. 栈内存分配越大越好吗?

可以虚拟参数指定(-Xss size 默认 1024kb)

不是,越大,则会线程数变小。物理内存是固定的。越大可以有益于递归的调用,会影响线程数目。

  1. 方法内的局部变量是否线程安全?

    私有的不用考虑线程安全。(判断是否是线程安全:1) 局部变量 2) 是否逃离了方法的管理 )

  • 如果方法内局部变量没有逃离方法的作用访问, 是线程安全的
  • 如果局部变量是引用对象,且逃离了作用访问,就需要考虑线程安全问题了

栈内存溢出 (StackOverflowError) 可调栈大小

  • 栈帧过多,(栈大小固定,栈帧多,放不下)如递归调用
  • 栈帧过大,

demo: json 转换时 打破 循环引用,在一方进行中断, @JsonIgnore 可以忽略这个属性的转换。

线程诊断(工具的使用)

  • cpu 占用过多

系统定位: top 定位 进程的占用cpu 过高, 用 ps 进行定位线程占用过高。 ps H -eo pid,tid | grep 进程ip
java 定位:jstack 进程ip 。可以获取所有的 线程,用ps 后的线程id(十进制) 进行确定线程 id (十六进制) 可以定位到运行的状态及出问题的源代码行数。

  • 程序运行很行时间没结果
    java 定位:jstack 进程ip。可以有死锁的一些线程提示。

本地方法栈(本地方法提供的空间)

  • 本地方法,不是由 java 实现的方法。因为有一些c /c++ 的方法。
  • 会非常的多。

堆 (线程共享区域)

  • 通过 new 关键字,创建的对象都会使用堆内存
  • 是线程共享的,堆中对象都需要考虑线程安全的问题
  • 有垃圾回收机制(不再被引用的对象会被回收)

堆内存溢出

  • 一直有标记,不能进行垃圾回收
  • outOfMemoryError java heap space
  • 设置 堆大小 -Xms8M

堆内存诊断

  1. jps工具:查看当前系统中有哪些 java 进程
  2. jmap 工具: 查看堆内存占用情况
  3. jconsle 工具: 图形界面的,多功能的检测工具,可以连续监测 (线程 ,cpu,堆)
system.gc() // 主动进行垃圾回收
jps 查看有哪些进程
jmap -heap 进程id  查看内存快照信息

案例:

垃圾回收后,内存占用仍然很高

jps
jmap pid 
jconsole 

jvisualvm: 可视化的工具查看虚拟机,可以查看 堆快照,查看 对象,查看元素,分析原因。