JVM(一)

java内存区域

public class kafka {
public static void main(String[] args) {
        ReplicaManager replicaManager = new ReplicaManager();
        replicaManager.loadReplicasFromDisk();
    }
}
public class ReplicaManager {
public void loadReplicasFromDisk() {
        Boolean hasFinishedLoad = false;
if (isLoacalDataCorrupt()) {
//....
        }
    }
public Boolean isLoacalDataCorrupt() {
        Boolean isCorrupt = false;
return isCorrupt;
    }
}

首先,你的JVM进程启动,首先会加载你的kafka类到内存中,然后有一个main线程,开始执行你的kafka中main方法

main线程是关联了一个程序计数器,那么他执行到哪一行行指令,就会记录到哪里

其次,就是main 线程在执行main方法的时候,会在main线程关联的java虚拟机栈里,压入一个main方法的帧栈,接着发现需要创建ReplicaManager类的实例对象,此时就会加载ReplicaManager类加载到内存中

然后一个Replication的对象实例分配到java堆内存,然后main方法的帧栈里的局部变量引入一个replication变量,让他引用ReplicaManager对象在java堆内存中的地址,

接着,main线程开始执行RelicaManager对象的方法,会一次执行到方法对应的帧栈中,执行完方法之后,再把方法对应的帧栈从java虚拟机栈里出栈。

双亲委派机制

扩展类加载器

Bootstrap ClassLoader,主要负责加载在安装的java目录下的核心类,在JDK安装目录下有一个lib目录,这个就是java的一些核心思路,支撑java系统的运行,

扩展类加载器

Extension ClassLoader,这个类加载器其实也是类似的,就是加载JDK安装目录下的lib\ext目录的一些类。

应用程序类加载器

这个就是负责加载classPath环境变量所指定路径的类,就是你写的代码,

自定义类加载器

根据你的需求加载你的类

双亲委派机制

JVM的类加载器是有亲子层级结构的,就是启动类加载器是最上层,扩展类加载器第二层,应用程序类加载器,最后一层就是自定义加载器

例如,JVM加载ReplicaManager类,此时用用程序类加载器就会问自己的爸爸,就是扩展类加载器,你能加载整个类吗,然后扩展加载器直接问自己的爸爸,启动类加载器,你能加载整个类吗,启动类加载器就会在自己的加载的目录寻找,发现没有找到,就会告诉自己的儿子,你去加载,然后扩展类加载器,也没有找到,就会告诉自己的儿子,你去加载,应用程序类加载器,就会看时候是自己的负责范围,果然是自己的,然后就会加载整个类到内存中去,这就是所谓的双亲委派模型,先找父亲加载,不行就去由儿子加载,这样的话,可以避免多层级的加载器结构重复加载某些类。

Tomcat这种web容器中类加载器如何设计实现

首先Tomcat类加载体系如下图,他是自定义了许多类加载器

Tomcat自定义了Common,Catalina,Shared等类加载器,其实就是用来加载tomcat自己的一些核心基础类库,然后tomcat每个部署在的web应用都有一个对应的WebApp类加载器,负责加载我们部署的这个Web应用的类,至于Jsp类加载器,则是给每个jsp都准备了一个Jsp类加载器,切记的是Tomcat是打破了双亲委派机制,

每个WebAPP负责加载对应的那个web引用的class文件,也即是我们写好的某个系统打包好的war包中的所有class文件,不会上传给上层类加载器去加载.

tomcat如何打破双亲委派机制

common类加载器,tomcat最基本的类加载器,加载的class可以被tomcat容器本身访问以及各个WebApp访问,实现共有类库,war和tomcat可以通用这个类class

cacalina类加载器,加载的webapp不可见,加载的是tomcat容器私有的类加载器,就是

shared类加载器,各个webapp共享类加载器,对于所有的webapp可见,但是对于Tomcat容器不可见,所有的webapp可以共用加载的类库,如上图的war1和war2使用同一个的mysql5.6的类,这个mysql就是share类加载器加载

webapp类加载器,各个webapp的私有加载器,仅对webapp可见,这个就是为了不同war包可能引用不同的版本,起到隔离的作用,

jsp类加载器,加载的仅仅是这个jsp所编译出来的class文件,当web容器检测到jsp修改了,然后新建一个jsp实例,就会替换旧的jsp实例

分代模型

我们知道我们代码写的对象大部分存活的周期极短,少数对象是长期存活的,JVM使用分代模型,存储不同的对象,将java堆内存划分为年轻代和老年代,

顾名思义,年轻代存放存放存活周期短的对象,老年代存放长期存在的对象

public class kafka {
    private static  ReplicaFetcher fetcher = new ReplicaFetcher();  
public static void main(String[] args) {
       loadReplicasFromDisk();        
        while (true){
           fetchReplicasFromRemote();
           Thread.sleep(2000);
       }        
    }    
private static void loadReplicasFromDisk(){
        ReplicaManager replicaManager = new ReplicaManager();
        replicaManager.load();
    }
public static void fetchReplicasFromRemote(){
        fetcher.fetch();
    }
}

例如代码,他们在java堆内存中是如何分布的,如下图

然后一旦loadReplicasFromDisk执行完毕之后,方法的帧栈就会出栈,对应的年轻代里的ReplicaManager对象也会被回收掉,如下图

永久代

JVM里的永久代其实就是我们之前说的方法区,可以理解就是所谓的永久代,你可以认为永久代就是放一些类信息的

方法区会进行垃圾回收吗,满足下面条件就可以回收引用

  1. 首先该类的所有实例对象都已经从java堆内存被回收
  2. 其次加载这个类的classLoader已经被回收
  3. 最后,对该类class对象没有任何应用

JVM内存中如何分配,如何流转

大部分的对象都是优先在新生代分配内存的,正如上面类静态变量fetcher用用的RelicaFetcher对象,会长期存活内存中,其实他刚开始的时候也是存活在新生代,最后经过多次垃圾回收之后,最后转移到老年代

那么什么时候进行垃圾回收

其实并不是java堆内存的对象没有引用,并不一定会发生垃圾回收,实际上垃圾回收也是需要条件的

常见的是当新生代预分配内存空间,几乎被对象占满了,但是还有对象创建,这个时候就要进行垃圾回收,新生代内存空间的垃圾回收称为Minor GC,有时候也叫 Young GC,这个时候就会回收没有任何引用的对象.

长期存活的对象会躲过多次垃圾回收,

比如上面说的ReplicaFetcher实例对象,他长期被静变量引用,所以他在新生代不会别回收,但是我们JVM有一条规定,当成功回收15次之后,还是没有回收,就会进入老年代,因此老年代放的就是年纪大的对象.

JVM核心参数

-Xms

java堆内存大小

-Xmx

java堆内存的最大大小

-Xmn

java堆内存新生代的大小

-X:PermSize

永久代大小

-X:MaxPermSize

永久代最大大小

-Xss

每个线程的栈内存大小

如何设置JVM堆内存

如上图一个支付系统,每天产生100万的订单,当然正常的生产环境不会在一台机器上,假设我们有三个机器,如下部署,每台机器每秒大概有30个请求

对于上面我们看看我们如何预估如何设置JVM堆内存大小

  1. 每条订单大约多少内存 我们的interger是4个字节,Long是8个字节等等,按此计算,假设我们的一个订单对象大概是500字节,不到1kb
  2. 实际估算要加10-20倍 由于我们创建订单不仅仅是订单对象,可能还有其他对象,比如我们的而此时的订单,30*500=1500,15kb,加上其他对象,每秒产生几百kb到1Mb
  3. 触发Minor GC 我就认为每秒产生1M,当过一段时间产生了几十万对象,此时占据了几百M内存,此时可能新生代快满了
  4. 此时如何设置JVM堆内存 假设我们机器是2核4G,机器本身就要占用内存空间,最后留给JVM的最多有2G,此时JVM还有方法区,栈内存,堆内存,留给堆内存可能只有1G,但是堆内存还分为新生代和老年代,留给新生代的空间假设只有500M,此时我们的系统每秒1M,不到500秒,新生代就满了,就会发生GC 这样频繁GC,是一种不好的现象,再比如我们用4核8G,再次计算半个小时到一个小时,才会发生一次Minor GC,其实我们也可以再多部署几台机器,比如5台机器,平均每天每秒也就20订单对象,这样对每台机器的请求越少,JVM压力越小

按照上面步骤合理的预估你们生产环境,将事半功倍。

如何堆内存设置不合适会出现什么情况

比如我们有一台2核4G机器,分给堆内存是1G,但是对于新生代就是500MB,正常情况下没有问题,但是在大促销的时候,我们就可以发现问题

假设每秒产生100个对象,每个对象500字节,就有50kb,此时我们要在此基础上乘以20倍,此时就是大约1M

而每秒产生1M,几百秒之后,新生代就会慢,此时就会发生Minor GC,频繁的发生GC,就会导致系统卡顿,体验不好

极端情况下,每秒1000个对象,最终系统内存每秒有10MB,甚至几十MB,同时系统的CPU,资源性能急剧下降,就会导致请求变慢,最后在Minor GC之后,但是还会有几十MB没有被回收,慢慢的就会导致进入老年代

如果进入老年代,那就会更加糟糕,因为老年代的GC,是非常慢的,老年代频繁的GC,最后有可能造成内存溢出,极大的影响性能

本文分享自微信公众号 - 洁癖是一只狗(rookie-dog),作者:洁癖汪

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-08-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JVM 《一 JVM 中的垃圾回收》

    当我们了解其中的内存之后,我们可能会有一点想法,我们的对象、相关类信息是存放在Java堆、方法区之中的。那我们的程序正在不断的new 对象、不断的loading...

    邹志全
  • 深入理解JVM(一)——JVM内存模型

    JVM内存模型 Java虚拟机(Java Virtual Machine=JVM)的内存空间分为五个部分,分别是: 1. 程序计数器 2. Java虚...

    大闲人柴毛毛
  • JVM(一)运行时数据区

    由于JAVA程序是交由JVM执行的,所以我们所说的JAVA内存区域划分也是指的JVM内存区域划分,JAVA程序具体执行的过程如下图所示。首先Java源代码文件会...

    周辰晨
  • JVM-一图让你全局了解JVM 原

    (adsbygoogle = window.adsbygoogle || []).push({});

    秋日芒草
  • JVM第一弹

    JVM是可运行java代码的假想计算机,包括一套字节码指令集,一组寄存器,一个栈,一个垃圾回收、堆和一个存储方法域。JVM是运行在操作系统之上的,它与硬件没有直...

    趣学程序-shaofeer
  • JVM(一):久识你名,初居我心

    JVM,一个熟悉又陌生的名词,从认识Java的第一天起,我们就会听到这个名字,在参加工作的前一两年,面试的时候还会经常被问到JDK,JRE,JVM这三者的区别。

    山禾说
  • JVM(一)史上最佳入门指南

    提到Java虚拟机(JVM),可能大部分人的第一印象是“难”,但当让我们真正走入“JVM世界”的时候,会发现其实问题并不像我们想象中的那么复杂。唯一真正令我们恐...

    Java中文社群-磊哥
  • JVM系列(一)—— 何为JVM

    JVM能够跨计算机系结构来执行JAVA字节码,主要是由于JVM屏蔽了与各个计算机平台相关的软件或硬件之间的差异,使得与平台相关的耦合统一由JVM提供者来实现。

    MickyInvQ
  • JVM学习(一)

    程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里字...

    一觉睡到小时候
  • JVM学习一

    jvm的内存结构:可以看到我们的java文件会首先编译成class文件,经过类加载器进行加载,然后经过jvm的相关区域:f方法区、堆、虚拟机栈、程序计数器、本地...

    路行的亚洲
  • JVM系列一:JVM内存组成及分配

    java内存组成介绍:堆(Heap)和非堆(Non-heap)内存 按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和...

    lyb-geek
  • 浅谈JVM(一) ClassLoader的双亲委派和沙箱机制

    转载自 https://blog.csdn.net/start_lie/article/details/79016312

    allsmallpig
  • JVM 学习笔记一 :JVM类加载机制

    1,类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。

    一枝花算不算浪漫
  • 十个问题弄清JVM&GC(一)

    每个java开发同学不管是日常工作中还是面试里,都会遇到JDK、JVM和GC的问题。本文会从以下10个问题为切入点,带着大家一起全面了解一下JVM的方方面面。

    宜信技术学院
  • GC 和 JVM的一些知识点

    JVM的内存结构包括五大区域:程序计数器、虚拟机栈、本地方法栈、堆区、方法区。其中程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭...

    java乐园
  • 十个问题弄清JVM&GC(一)

    每个java开发同学不管是日常工作中还是面试里,都会遇到JDK、JVM和GC的问题。本文会从以下10个问题为切入点,带着大家一起全面了解一下JVM的方方面面。

    宜信技术学院
  • jvm原理——第一篇jvm的运行模式

    JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模...

    胡齐
  • JVM学习笔记(一)- JVM编译运行机制

    上图从左往右依次描述的是Java源码编写到编译再到Java虚拟机的ClassLoader加载并执行的全部过程。

    云叶知秋
  • JVM(2)--一文读懂垃圾回收

    与其他语言相比,例如c/c++,我们都知道,java虚拟机对于程序中产生的垃圾,虚拟机是会自动帮我们进行清除管理的,而像c/c++这些语言平台则需要程序员自己手...

    帅地

扫码关注云+社区

领取腾讯云代金券