JVM

java 虚拟机 (java virtual machine JVM) ,一种能够运行 java 字节码的虚拟机。作为一种编程语言的虚拟机,实际上
不只是专用于 Java 语言,只要生成的编译文件匹配 JVM 对加载编译文件格式要求,任何语言都可以由 JVM 编译运行。
比如 kotlin scala 等。
jvm 有很多,不只是 Hotspot ,还有 JRockit J9 等等。
image.png
 
类加载器将.class文件加载进系统,将.class文件解析,生成的是什么?就是将类的元信息是如何存储的。(InstanceKlass)
InstanceKlass:java类(非数组)元空间。
    InstanceMirrorKlass:镜像类,class对象(堆区)。
    InstanceRefKlass:引用。
    InstanceClassLoaderKlass:
ArrayKlass:存储数组类的元信息
    TypeArrayKlass: 基本类型的数组在jvm中的存在形式
     ObjArrayKlass:  引用类型的数组在jvm中的存在形式
java中的数组:
     1:静态数据类型    JVM内置的8中数据类型
     2:动态数据类型  (对)运行时动态生成的

 
java程序在内存中的存储分配情况:
 
堆:对象,字符串常量池
栈:局部变量
方法区: 对象的引用 ,静态变量 + 常量 + 类信息(构造方法 / 接口定义) + 运行时常量池都存在方法区中

一、堆区: 
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令) 
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身 
栈区: 
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中 
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。 
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。 
方法区: 
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。 
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
实际情况推荐你用jstat和jmap查看分配情况。不同虚拟机在定义和分配上有些差异。但有一点,字符串常量的对象最新还是在堆创建,跟方法区在一起的只是一些基本类型数据包括引用地址

jvm的基本结构:

jvm由三个主要的子系统构成:
   1:类加载系统。2:运行时数据区(内存结构)。3:执行引擎

内存结构图:

堆和方法区是线程共享的。
栈、本地方法栈、程序计数器是线程私有的,随着线程的产生而产生。
 
public class Demo {
	public int math(){
		int a = 1;
		int b = 2;
		int c = a + b;
		return c;
	}
	public static void main(String[] args) {
		Demo de = new Demo(); 
		de.math();
	}
}

栈:存放局部变量

栈里面存放的都是栈帧,栈帧可以理解为一个方法所占的区域。区域中分为局部变量表、操作数栈、动态链接、方法出口。
局部变量表:存放的是我们定义的局部变量。
操作数栈:用来做操作的一块内存区域。
程序计数器:就是一个指针,用来存储指向下一条执行代码的地址。
方法出口:记录了调用方法的地址,所以math能够返回到main方法。
动态链接:暂且理解为栈指向堆内存的指针。

方法区:静态变量+常量+类信息(构造方法/接口定义)+运行时常量池都存在方法区中

类的所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在这里定义。简单来说,所有定义的方法的
信息都保存在该区域,静态变量 + 常量 + 类信息(构造方法 / 接口定义) + 运行时常量池都存在方法区中,虽然 Java 虚拟
机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap (非堆),目的应该是为了和 Java
堆区分开

元空间使用的是本地的物理内存。以前是放在堆里面的。
private Static User user = new User();user引用放在方法区,new出来的对象放在堆里面

元空间--堆外内存,以前叫永久代。

JDK1.8 之后,元空间替代了永久代,它是对 JVM 规范中方法区的实现,区别在于元数据区不在虚拟机当中,而是用
的本地内存,永久代在虚拟机当中,永久代逻辑结构上也属于堆,但是物理上不属于。

堆:eden:伊甸区,from suvivor + to suvivor 幸存者区,元空间。

虚拟机启动时自动分配创建,用于存放对象的实例,几乎所有对象(包括常量池)都在堆上分配内存,当对象无法在
该空间申请到内存是将抛出 OutOfMemoryError 异常。同时也是垃圾收集器管理的主要区域。

eden:伊甸区
from suvivor:
to suvivor:

'
新生代( Young Generation
类出生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,
结束生命。
新生代分为两部分:伊甸区( Eden space )和幸存者区( Survivor space ),所有的类都是在伊甸区被 new 出来的。
幸存区又分为 From To 区。当 Eden 区的空间用完时,程序又需要创建对象, JVM 的垃圾回收器将 Eden 区进行垃圾回
收( Minor GC ),将 Eden 区中的不再被其它对象应用的对象进行销毁。然后将 Eden 区中剩余的对象移到 From
Survivor 区。若 From Survivor 区也满了,再对该区进行垃圾回收,然后移动到 To Survivor 区。
老年代( Old Generation
新生代经过多次 GC <15次>仍然存在的对象移动到老年区。若老年代也满了,这时候将发生 Major GC (也可以叫 Full GC ),
进行老年区的内存清理。若老年区执行了 Full GC 之后发现依然无法进行对象的保存,就会抛出
OOM (OutOfMemoryError)异常

 

垃圾收集算法:

标记-清除算法:
  它是最基础的的收集算法,这个算法分为两个阶段,标记和清除。首先标记处所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它有两个不足的地方:
1:效率问题,标记和清除的两个过程的效率不高。
2:空间问题,标记清除之后会产生大量不连续的碎片。

复制算法:使用与新生代,因为每次都会浪费一半的内存,老年代里面的对象都比较大,如果在浪费一半内存会moner gc。

标记-整理算法:先移动在清理。适合老年代

日志收集:


-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径

-xx:+PrintGCDetails -xx:+PrintGCDateStamps -xloggc ./gc.log
使用gceasy图形化界面进行分析。
 

-Xss: 每个线程的栈大小
-Xms: 初始堆大小,默认物理内存的 1/64
-Xmx: 最大堆大小,默认物理内存的 1/4
-Xmn: 新生代大小 -XX:NewSize: 设置新生代初始大小
-XX:NewRatio: 默认 2 表示新生代占年老代的 1/2 ,占整个堆内存的 1/3
-XX:SurvivorRatio: 默认 8 表示一个 survivor 区占用 1/8 Eden 内存,即 1/10 的新生代内存。
-XX:MetaspaceSize: 设置元空间大小
-XX:MaxMetaspaceSize: 设置元空间最大允许大小,默认不受限制, JVM Metaspace 会进行动态扩展。

啥是oom :https://blog.csdn.net/paopaoandguoer/article/details/76206586

垃圾收集器:

     1:serial  串行收集器  年轻代:复制算法,老年代:标记整理算法

     2:  paraller  并行收集器  年轻代:复制算法,老年代:标记整理算法

     3:  parNew  并行收集器  年轻代:复制算法,老年代:标记整理算法

     4:  cms  并行收集器  只适用老年代:标记-清理算法
          初始标记,并发标记,重新标记,并发清理,并发重置

     5:  g1  复制算法
          g1收集器会将堆分为很多个独立的区域-->region,region最多有2048个,region的大小就是堆内存/2048=region,这个大小可以设置,-XX:G1HeapRegionSize 2M,
    region主要分为4种类型,Eden -- 伊甸区,Survivor,Old -- 老年代,Humongous -- 大对象
    Edon区对堆的占比就是5%,但是Survivor的比例还是8:1:1  Eden:s0:s1,主要的区别就是对大对象的收集,当一个对象的大小超过了Humongous的50%,就是直接发到Humongous区域,当超过了多个Hm就是放入到连续几个hm,这样就避免了大对象进    入老年代,引起频繁的FullGC。
   发生一次GC运行的几个步骤:
        1:初始标记(init mark,swt) 暂停所有线程,并记录下GCRoot能直接引用的对象
        2:并发标记(conc mark) 开始寻找GCROOT间接引用的对象---可达性分析。用户线程和垃圾回收线程一起运行,就可能造成刚才标记的对象发生变化
        3:最终标记(remark,swt)停止用户线程,重新标记因为并发标记影响的的对象。
        4:筛选回收(clean up,swt) 根据用户设置的停顿时间来筛选需要最需要回收的Region,回收算法主要用的是复制算法, 将一个region中的存活对象复制到另一个region中, 这种不会像CMS那样 回收完因为有很多内存碎片还需要整理一次, G1采用复制算法回收几乎不会有太多内存碎片。

GCRoot可达性分析:https://blog.csdn.net/weixin_41910694/article/details/90706652

垃圾回收实战:

ThreadLocal详解:

      强引用:A a = new A();直接引用,在gc的时候不会回收。
      软引用:SoftReference<byte[]> m = new SoftReference<>(new byte[1024 * 1024 *10]); gc的时候内存不够的时候被回收。
      弱引用: WeakReference<Object> wr = new WeakReference<Object>(new Object());  gc的时候不管内存够不够都会被回收。
      虚引用:https://www.cnblogs.com/mfrank/p/9837070.html 当一个对象和GCROOT对象直接存在一个虚引用,这个对象就叫 虚可达,这个对象在垃圾回收的时候,gc会先判断如果有虚引用就会把它放到一个引用队列里面。

threadLocal的内存泄露:
    threadLocal介绍:ThreadLocal是JDK提供的一个保证线程安全的工具类,代表一个线程的局部变量。把数据放在ThreadLocal中,每个线程只能操作自己的数据。实现了数据的隔离性,底层用的map,key是当前线程。
    threadLocal造成的内存泄露:底层是一个threadLocalMap   key是weakRefence弱引用,gc的时候key会回收,value不会回收,所以用调用remove。
    什么是内存泄漏:当一个程序申请内存之后,无法去释放这个内存,多次之后内存空间占用的越来越大,就会造成内存溢出,比如说一个对象申请内存之后,一直被GCROOT对象强引用着,jvm就不会去回收这个对象。

三、线程栈

栈(JVM Stack)存放主要是栈帧( 局部变量表, 操作数栈 , 动态链接 , 方法出口信息 )的地方。注意区分栈和栈帧:栈里包含栈帧。

与线程栈相关的内存异常有两个:

a)、StackOverflowError(方法调用层次太深,内存不够新建栈帧)

b)、OutOfMemoryError(线程太多,内存不够新建线程)

1、java.lang.StackOverflowError

栈溢出抛出java.lang.StackOverflowError错误,出现此种情况是因为方法运行的时候,请求新建栈帧时,

栈所剩空间小于战帧所需空间。

例如,通过递归调用方法,不停的产生栈帧,一直把栈空间堆满,直到抛出异常 :

jvm调优

使用jstat -gc 进程号,


使用jstat -gc  12345 1000 10,每隔1000毫秒执行以下这个命令,共执行10次。

jvm和string类型的存放:

https://blog.csdn.net/u011635492/article/details/81046174

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值