初级jvm

1.JVM的位置

1.1.哪些地方不会有垃圾回收呢?

2.类加载器

作用:加载class文件

图解:实例化对象过程

2.1.类加载器

2.2.双亲委派机制

2.3.为什么使用双亲委派机制

2.4.类装载的执行过程

2.4.1.加载

2.4.2.连接

2.4.2.1.验证
2.4.2.2.准备
2.4.2.3.验证

2.4.3.初始化

2.4.4.使用

2.4.5.卸载

3.Native、方法区

native

凡是native修饰的方法,说明java的作用范围达不到,回去调用 C语言的库,会进入本地方法栈

方法区(重要)

方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,比如构造函数,接口代码也在此定义。简单来说,所有定义方法的信息都保存在该区域,次区域属于共享区间

静态变量、常量、类信息(构造方法,类定义)、运行时的常量池保存在方法区中。但是实例变量存在堆内存,和方法区无关

方法区就存这些东西:static、final,class,常量池

4.栈

栈:先进后出,后进先出:主管程序的运行,生命周期和线程结束,线程结束,栈内存就释放了,对于栈来说不存在垃圾回收。

队列:先进先出,后进后出

为什么main方法先调用后执行?

栈溢出概念

栈执行原理:栈帧

堆、栈、方法区交互关系

图解:代码new了class(对象)后,会放到栈内存,赋值后,实际数据是存放在堆内存的。通过栈的地址值去堆中找到具体的指,堆中的常量是存放在方法区中的常量池的

对象实例化过程

 1.堆内存中存放对象的属性,栈内存中存放的是引用变量

 2.堆内存中为new Person()开辟空间的地址BE2500,是栈内存中引用变量p的值

子类对象的实例化过程

1.因为先开辟子类的内存空间,故先使子类构造方法进栈

2.因为子类的构造方法中使用了super关键字,要执行父类的构造方法,所以就要在执行父类的构 造方法之前,先显示初始化父类的属性

3.执行完父类构造方法后出栈,再显示初始化子类的属性

5.堆

 HotSpot:一个jvm只有一个堆,大小是可以调节的

类加载器读取一个文件后会把什么存放在堆里呢?

类、方法、变量、常量,保存我们所有引用类型真实变量。

三个区域

新生区(伊甸园区)、老年区、永久区

GC垃圾回收主要是在伊甸园去和老年区(新生区主要是轻量级GC,老年代主要是重量级)

在jdk1.8以后,永久存储区叫做元空间,不在jvm中,而是在本地内存中

新生区

类的创建和死亡的地方

伊甸园区

所有对象都是在这里new出来的,用完后如果gc未能回收,则进入幸存区

幸存区

分为幸存0区和幸存1区:当伊甸园区轻量gc未清理掉的垃圾会进入幸存区

养老区

新生区满了会进入此区域进行重GC

永久区

这个区域常住存在,用来存储一些jdk自带的class对象、元数据、java运行时的环境或者类型,这个区域不存在垃圾回收,当关闭jvm虚拟机,这个内存就会释放

jdk1.6:永久带,常量池在堆中

jdk1.7:永久带,慢慢退化,常量池在堆中

jdk1.8:无永久区,常量池在原空间

内存和调参

package com.cn.juc;

public class LazyMan {
    public static void main(String[] args) {
        //虚拟机试图使用的最大内存
        long max = Runtime.getRuntime().maxMemory();
        //JVM总内存
        long total = Runtime.getRuntime().totalMemory();

        System.out.println("max=" + max + "字节\t" + (max/(double)1024/1024) +"MB");
        System.out.println("total=" + total + "字节\t" + (total/(double)1024/1024) +"MB");
    }
}

默认情况下:分配的总内存是电脑内存的1/4。而初始化的内存:1/64

OOM排查思路

1.尝试扩大内存

2.如果还是oom,就要分析内存,看下哪个地方出现了问题

-Xms1024m -Xmx1024m -XX:+PrintGCDetails

-Xms:初始内存 

-Xmx:总内存

-XX:+PrintGCDetails:打印GC的过程信息

package com.cn.juc;

public class LazyMan {
    public static void main(String[] args) {
        //虚拟机试图使用的最大内存
        long max = Runtime.getRuntime().maxMemory();
        //JVM总内存
        long total = Runtime.getRuntime().totalMemory();

        System.out.println("max=" + max + "字节\t" + (max/(double)1024/1024) +"MB");
        System.out.println("total=" + total + "字节\t" + (total/(double)1024/1024) +"MB");
    }
}

计算可以知:新生带 + 老年带 = 总内存 -> 云空间逻辑存在,实际不存在堆内存

GC:垃圾回收机制

jvm在进行GC时,并不是对三个区域统一回收,大部分回收都是在新生带

类型:轻GC,重GC(fullGC)

引用计数法

图解:括号的下边为对象的使用次数,当次数为0证明未被使用,做gc处理

缺点:目前不用这种算法,效率不高,而且本身也占内存

复制算法

好处:没有内存碎片

坏处:浪费了内存空间:幸存to区空间永远是空。假设对象100%存活,复制算法就不合理

复制算法最佳使用场景:对象存活较低的时候。

标记清除算法

优点:不需要额外空间

缺点:两次扫描验证浪费时间,会产生内存碎片

标记压缩算法

GC总结

内存效率(时间复杂度):复制算法>标记清除算法>标记压缩算法

内存整齐度:复制算法=标记压缩算法>标记清楚算法

内存利用率:标记压缩算法=标记清除算法>复制算法

没有最好的算法,只有最合适的算法

GC一般采用分代收集算法

年轻代:对象存活率,一般采用复制算法

老年代:存活率高,区域大。一般采用标记清除(内存碎片不是太多)+标记压缩混合实现

JMM(java内存模型)

它描述的是和多线程相关的一组规范,需要各个 JVM 的实现来遵守 JMM 规范,以便于开发者可以利用这些规范,更方便地开发多线程程序。这样一来,即便同一个程序在不同的虚拟机上运行,得到的程序结果也是一致的。(缓存一致性协议,用于定义数据读写的规则)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值