黑马JVM学习笔记二


title: 黑马JVM学习笔记二
date: 2020-01-13 1:00:00
tags:

  • JVM

categories:

  • JVM
  • 黑马jvm

内存结构

内存结构总览图:
黑马JVM学习笔记二_第1张图片
黑马JVM学习笔记二_第2张图片

程序计数器

1.定义:Program Counter Register 程序计数器 (寄存器)

程序计数器是Java对物理硬件的一些屏蔽和抽象,物理上通过寄存器来实现,寄存器是整个cpu里读取最快的一个单元。

2.作用:记住下一条jvm指令的执行地址
黑马JVM学习笔记二_第3张图片
3.特点

  • 线程是私有的,多线程之间分别有各自的程序计数器记录对应线程的执行位置
  • 程序计数器是Java虚拟机规范中唯一一个不会存在内存溢出的区(堆和栈等会出现内存溢出现象)

虚拟机栈(Java Virtural Machine Stacks)

栈、栈帧以及代码演示

1.栈是一种数据结构,特点是先进后出。
2.虚拟机栈就是线程运行时需要的内存空间。一个线程需要一个对应的栈,多个线程就需要多个虚拟机栈。
3.一个栈又是由多个栈帧组成,一个栈帧就对应着Java中一个方法的调用,即栈帧就是每个方法运行时需要的内存。每个方法运行时需要的内存一般有参数,局部变量,返回地址,这些都需要占用内存,所以每个方法执行时,都要预先把这些内存分配好。
4.每个线程只能由一个活动栈帧,对应着当前正在执行的那个方法。(位于栈顶)

黑马JVM学习笔记二_第4张图片
黑马JVM学习笔记二_第5张图片

栈问题辨析

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

不涉及,垃圾回收只是回收堆内存中的无用对象,占内存不需要对它执行垃圾回收,随着方法的调用结束,栈内存就释放了。

2.栈内存分配越大越好么?

  • 首先栈内存可以指定:-Xss size(如果不指定栈内存大小,不同系统会有一个不同的默认值)
  • 其次由于电脑内存一定,假如有100Mb,如果给栈内存指定为2Mb,则最多只能存在50个线程,所以并不是越大越好,栈内存较大一般是可以进行较多次的方法递归调用,而不会增强线程效率,反而会使线程数量减少,一般使用默认大小。

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

  • 看一个变量是否线程安全,首先就是看这个变量对多个线程是共享的还是私有的,共享的变量需要考虑线程安全。
  • 其次局部变量也不能保证是线程安全的,需要看此变量是否逃离了方法的作用范围(作为参数和返回值逃出方法作用范围时需要考虑线程安全问题)
    黑马JVM学习笔记二_第6张图片

栈内存溢出(StackOverflowError)

什么情况下会导致栈内存溢出呐?
1.栈帧过多导致栈内存溢出(一般递归调用次数太多,进栈太多导致溢出)
2.栈帧过大导致栈内存溢出(不太容易出现)

栈内存溢出代码演示1(自己开发代码):
黑马JVM学习笔记二_第7张图片
idea中设置栈内存大小:
黑马JVM学习笔记二_第8张图片
栈内存溢出代码演示2(第三方依赖库出现):
黑马JVM学习笔记二_第9张图片
本案例可以使用JsonIgnore注解解决循环依赖,数据转换时,只让部门类去关联员工类,员工类不再关联部门类,在员工类的部门属性(dept)上加@JsonIgnore注解。具体使用详情可以点击此处查看

线程运行诊断

案例1:cpu占用过多(linux系统为例)

排查步骤:

1.在linux中使用top命令,去查看后台进程对cpu的占用情况

在这里插入图片描述

2.查看线程对cpu的占用情况:ps H -eo pid,tid,%cpu
如果显示过多,可使用ps H -eo pid,tid,%cpu | grep 进程id,过滤掉不想看的部分进程

黑马JVM学习笔记二_第10张图片

3.定位到是哪个线程占用内存过高后,再使用Jdk提供的命令(jstack+进程id)去查看进程中各线程的运行信息,需要把第二步中查到的线程id(十进制)转为十六进制,然后进行比较查询到位置后判断异常信息。

黑马JVM学习笔记二_第11张图片

黑马JVM学习笔记二_第12张图片

案例2:线程诊断_迟迟得不到结果

  • 仍然通过jdk提供的 jstack+进程id的方式,去查看进程中各个线程的运行信息
    黑马JVM学习笔记二_第13张图片
    黑马JVM学习笔记二_第14张图片

本地方法栈

含义:Java虚拟机调用本地方法时,需要给本地方法提供的一些内存空间

堆(heap)

定义

  • 虚拟机栈,程序计数器,本地方法栈,这些都是线程私有的,而堆和方法区,是线程公用的一块内存区域。
  • 通过new关键字创建的对象都会使用堆内存
  • 由于堆是线程共享的,堆内的对象都要考虑线程安全问题(也有一些例外)
  • 堆有垃圾回收机制,不再被引用的对象会被回收

堆内存溢出(OutOfMemoryError:Java heap space)

对象一直存在于堆中未被回收,且占用内存越来越大,最终导致堆内存溢出

黑马JVM学习笔记二_第15张图片

堆内存诊断

1.jps工具:jps,查看当前进程中有哪些Java进程,并将进程id显示出来(idea中通过terminal命令行输入命令)
黑马JVM学习笔记二_第16张图片
2.jmap工具:jmap -heap 进程id 查询某一个时刻堆内存的占用情况
在这里插入图片描述
3.jconsole工具

  • 图形界面的,多功能监测工具,可连续监测,使用流程图如下(1-2-3):
    黑马JVM学习笔记二_第17张图片
    4.jvisualvm工具
  • 也是以中图形界面化的工具,并且可以看到堆内存中具体对象占用内存情况,使用堆dump,类似于一种快照,分析某一时刻的内存情况。
    黑马JVM学习笔记二_第18张图片

方法区

定义

  • 方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,他用于存储已被虚拟机加载的类信息、常量、静态常量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是他却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。
  • 对于习惯在HotSpot虚拟机上开发、部署程序的开发者来说,很多都更愿意把方法取称为“永久代”(Permanent Generation),本质上两者并不等价,仅仅是因为HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已,这样HotSpot的垃圾收集器可以像管理Java堆一样管理这部分内存,能够省去专门为方法区编写内存管理代码的工作。对于其他虚拟机(如BEA JRockit、IBM J9等)来说是不存在永久代的概念的。原则上,如何实现方法区属于虚拟机实现细节,不受虚拟机规范约束,但使用永久代来实现方法区,现在看来并不是一个好主意,因为这样更容易遇到内存溢出问题(永久代有-XX:MaxPermSize的上限,J9和JRockit只要没有触碰到进程可用内存的上限,例如32位系统中的4GB,就不会出现问题),而且有极少数方法(例如String.intern())会因这个原因导致不同虚拟机下有不同的表现。因此,对于HotSpot虚拟机,根据官方发布的路线图信息,现在也已放弃永久代并逐步改为采用Navtive Memory来实现方法区的规划,在JDK1.7的HostSpot中,已经把原本放在永久代的字符串常量池移出,jdk1.8中后称作元空间,用的操作系统内存。
  • Java虚拟机规范对方法区的限制非常宽松,除了和Java堆一样不需要连续的内存和可以喧嚣而固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样“永久”存在了。这区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说,这个区域的回收“成绩”比较难以令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收确实是必要的。在Sun公司的BUG列表中,曾出现过的若干个严重的BUG就是由于低版本的HotSpot虚拟机对此区域未完全回收而导致内存泄漏。
  • 根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

英文原文关于虚拟机的定义:
黑马JVM学习笔记二_第19张图片

组成

jdk1.8之前,方法区是用的堆内存,1.8之后,方法区用的操作系统内存。
这块不是太清晰,可以参考下此篇博客 点击查看

常量池分为静态常量池和动态常量池,下图中的常量池指的是动态常量池,因为它们已经被读入内存中去,而静态常量池存在于class文件中

黑马JVM学习笔记二_第20张图片

方法区内存溢出

通过自定义类加载器,不断的去加载类,读取到方法区,演示方法区内存溢出
黑马JVM学习笔记二_第21张图片

jdk1.8和1.8之前报错内容区别如下

黑马JVM学习笔记二_第22张图片

常量池

在这里插入图片描述

常量池分类

常量池大体可以分为:静态常量池,运行时常量池。

  • 静态常量池 存在于class文件中,比如经常使用的javap -verbose中
  • 运行时常量池呢,就是在class文件被加载进了内存之后,常量池保存在了方法区中,通常说的常量池 值的是运行时常量池。所以呢,讨论的都是运行时常量池

针对常量池案例演示时补充内容:编译和反编译

黑马JVM学习笔记二_第23张图片
点击查看 Java文件编译与反编译

StringTable(字符串常量池)

1.先看几道面试题:
黑马JVM学习笔记二_第24张图片
这一部分是有点懵逼,看这篇博客啦点击查看

6.直接内存

直接内存是操作系统的内存,并不属于Java虚拟机

特点直接内存是操作系统的内存,并不属于Java虚拟机

你可能感兴趣的:(2020年以前文章)