JVM内存结构

JVM虚拟内存结构可以分为公有和私有两部分.

公有部分内存

公有指所有线程共享的部分: Java堆,方法区,常量池

Java堆

用于Java实例对象的内存分配的区域.几乎所有的实例对象都会在这里进行内存分配(栈上分配)

Java 堆根据对象存活时间的不同,Java 堆还被分为年轻代、老年代两个区域,年轻代还被进一步划分为 Eden 区、From Survivor 0、To Survivor 1 区

JVM堆

当有对象需要分配时,一个对象永远优先被分配在年轻代的 Eden 区,等到 Eden 区域内存不够时,Java 虚拟机会启动垃圾回收。此时 Eden 区中没有被引用的对象的内存就会被回收,而一些存活时间较长的对象则会进入到老年代。在 JVM 中有一个名为 -XX:MaxTenuringThreshold 的参数专门用来设置晋升到老年代所需要经历的 GC 次数,即在年轻代的对象经过了指定次数的 GC 后,将在下次 GC 时进入老年代

默认的虚拟机配置,Eden:from :to = 8:1:1

Read More

jinfo

全称Java Configuration Info. 主要作用是实时查看和调整JVM配置参数

查看JVM参数

用法:jinfo -flag <name> PID 例:jinfo -flag ThreadStackSize 18348,得到结果-XX:ThreadStackSize=256,即Xss为256K

调整JVM参数

用法:
如果是布尔类型的JVM参数: jinfo -flag [+|-]<name> PID,enable or disable the named VM flag

如果是数字/字符串类型的JVM参数 jinfo -flag <name>=<value> PID,to set the named VM flag to the given value

查看所有支持动态修改的JVM参数

java -XX:+PrintFlagsInitial | grep manageable

Read More

jcmd

可以用它来导出堆、查看Java进程、导出线程信息、执行GC、还可以进行采样分析

查看进程

命令:jcmd -l,等价于jcmd,jps

查看性能统计

jcmd pid PerfCounter.print

Read More

JVM参数

perfma JVM参数线上解析

HeapSize

512*page_size 对齐,page_size通常情况下是4kb,如果取值不是其倍数,向上取整

Xmx

堆的最大值

MaxHeapSize等价

Xms

设置堆的最小值(堆的初始值)
InitialHeapSize等价

Xminf/Xmaxf

等价于MinHeapFreeRatio/MaxHeapFreeRatio,默认值分别为40/80

G1下作用于整个heap,其他算法作用于老生代,GC后根据使用情况对堆内存进行扩/缩容

MinHeapDeltaBytes

尝试扩容/缩容的时候,决定是否要做或尝试扩容的时候最小扩多少,默认为192k

Read More

线程

现代操作系统的最小操作单元

线程的状态

  • NEW: 初始状态,线程被构建,但是还没有调用start()方法
  • RUNABLE: 运行状态,就绪和运行中两种状态统称为’运行中’
  • BLOCKED: 阻塞状态
  • WAITING: 等待状态,表示线程进入等待状态.进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知/中断)
  • TIME_WAITING: 超时等待状态,不同于WAITING,可以在指定时间自行返回
  • TERMINATED: 终止状态,表示当前线程已经执行完成

线程状态迁移

Daemon线程

Daemon线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。这 意味着,当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出。可以通过调 用Thread.setDaemon(true)将线程设置为Daemon线程

Read More

happens-before

重排序根据是否会改变程序执行结果分为:

  • 会改变程序执行结果的重排序,JMM要求编译器和处理器必须禁止这种重排序
  • 不会改变程序执行结果的重排序

对happens-before关系的定义如下:

  1. 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作
    可见,而且第一个操作的执行顺序排在第二个操作之前
  2. 两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照 happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系 来执行的结果一致,那么这种重排序并不非法

happens-before规则:

  1. 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作
  2. 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁
  3. volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的 读
  4. 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C
  5. start()规则:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作
  6. join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回

文章链接 https://fangzongzhou.github.io/2020/10/19/计算机/技术栈/Java/并发编程/happens-before/

final域

对于final域,编译器和处理器需要遵守两个原则

  • 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序
  • 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序

写final域的重排序规则禁止把final域的写重排序到构造函数之外,写final域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被 正确初始化过了,而普通域不具有这个保障

  • JMM禁止编译器把final域的写重排序到构造函数之外
  • 编译器会在final域的写之后,构造函数return之前,插入一个StoreStore屏障

读final域重排序规则: 在一个线程中,初次读对象引用与初次读包含该对象的final域,这两个操作会被禁止处理器重排序. 编译器会在读final域操作前插入一个LoadLoad屏障. 读final域的重排序规则可以确保:在读一个对象的final域之前,一定会先读包含这个final 域的对象的引用

对于引用类型,写final域的重 排序规则对编译器和处理器增加了如下约束:在构造函数内对一个final引用的对象的成员域 的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序

文章链接 https://fangzongzhou.github.io/2020/10/19/计算机/技术栈/Java/并发编程/final域/

锁可以让临界区互斥执行

锁的内存语义

锁是Java并发编程中重要的同步机制. 锁除了可以让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息

线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到内存中.

线程获取锁时,JMM会把该线程对应的本地内存置为无效.从而使得临界区代码必须从主内存中读取共享变量

锁获取与volatile读有相同的内存语义

文章链接 https://fangzongzhou.github.io/2020/10/19/计算机/技术栈/Java/并发编程/锁/