zoukankan      html  css  js  c++  java
  • JVM内存管理 《深入分析java web 技术内幕》第八章

    • 8.1 物理内存与虚拟内存
    物理内存RAM(随机存储器),寄存单元为寄存器,用于存储计算单元执行指令的中间结果。
    连接处理器和RAM或者处理器和寄存器的是地址总线,这个地址的宽度影响了物理地址的索引范围,总线的宽度决定了处理器一次可以从寄存器或者内存中获取多少个bit。
    虚拟内存的出现使不同进程在同时运行时可以共享物理内存,提高内存利用率,而且能扩展内存的地址空间。
    • 8.2 内核空间与用户空间
    一个电脑4GB的地址空间被划分为内核空间和用户空间,程序只能使用用户空间的内存。
    内核空间主要是指操作系统运行时所使用的用于程序调度、虚拟内存的使用或者连接硬件资源等的程序逻辑。
    • 8.3 Java中那些组件需要使用内存
    Java堆
    Java堆是用于存储Java对象的内存区域,堆的大小在JVM启动时就一次向操作系统申请完成,通过-Xmx(最大)和-Xms(初始)两个选项来控制大小,一旦分配完成后就固定了。Java堆中内存空间的管理由JVM来控制,对象创建由Java应用程序控制,对象所占的空间释放由管理内存的垃圾收集器来完成。
    线程
    JVM运行实际程序的实体是线程,而线程需要内存空间来存储必要的数据。每个线程创建时JVM都会为它创建一个堆栈。
    类和类的加载器
    Java中类和加载类本身同样需要存储空间,这个区域叫永久带(PermGen区)。
    卸载类(内存回收)条件:
    1、Java堆中没有对表示该加载器的java.lang.ClassLoader对象引用
    2、Java堆没有对表示类加载器的类的任何java.lang.Class对象的引用
    3、在Java堆上该类加载器加载的任何类的所有对象都不在存活(被引用)
    NIO
    NIO使用java.nio.ByteBuffer.allocateDirect()方法分配内存,是本机内存而不是Java堆上的内存,增加了一次系统调用。直接ByteBuffer产生的数据和网络或者磁盘交互都在操作系统的内核空间中发生,不需要将数据复制到Java内存中,加快数据处理速度。
    JNI
    JNI技术使得本机代码(C语言)可以调用Java方法,即native memory
    • 8.4 JVM内存结构
    在Java虚拟机规范中将Java运行时数据划分为6种
    PC寄存器数据
    保存当前执行的程序的内存地址
    Java栈
    Java栈总是和线程关联,每当创建一个线程时,JVM就会为这个线程创建一个对应的Java栈,这个栈中又会含有多个栈帧,这些栈帧是与每个方法关联起来,每运行一个方法就创建一个栈帧,每一个栈帧会含有一些内存变量,操作栈和方法返回值等信息。
    堆是存放Java对象的地方,是JVM管理Java对象的核心内存区域,每个存储在堆中的Java对象都是这个对象的类的一个副本,它会复制包括继承自它父类的所有非静态属性
    方法区
    JVM方法区用于存储类结构信息的地方,在Java堆中的永久区内,在启动程序后一段时间就固定饿了
    本地方法栈
    是为JVM运行Native方法准备的空间,由于很多Native方法是C语言实现的,所以也叫C栈
    运行时常量池
    代表运行时每个class文件中的常量表
    • 8.5 JVM内存分配策略
    通常的内存分配策略
    1、静态内存分配:指在程序编译时就能确定每个数据在运行时刻的存储空间需求
    2、栈式内存分配(动态存储分配):由一个类似堆栈的运行栈来实现,程序对数据区的需求在运行时才能够为其分配内存。栈式内存分配按照先进后出的原则进行分配
    3、堆内存分配:当程序真正运行到相应的代码时才知道空间大小
    Java中内存分配详解
    Java栈:其分配是和线程绑定在一起的,当我们创建一个线程时,JVM会为这个线程创建一个新的Java栈,一个线程的方法的调用和返回对应于这个Java栈的压栈和出栈。当线程激活一个Java方法时,JVM就会在线程的Java堆栈里新压入一个帧,这个帧自然成了当前帧。在此方法执行期间,这个帧用来保存参数、局部变量、中间计算过程和其他数据。栈中主要存放一些基本的数据类型和对象句柄(引用)。存取速度比堆快,仅次于寄存器,栈数据可以共享。静态分配内存,编译时确定生存期大小。
    Java堆:每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用程序所有的线程共享。Java中分配堆内存是自动初始化,所有对象的存储空间都是在堆中分配的,但是这个对象的引用在堆栈中分配。可动态分配内存大小、运行时分配内存。
    即创建一个对象时,堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是指向这个堆对象的指针(引用)而已。
    • 8.6 JVM内存回收策略
    静态内存分配和回收
    Java中静态内存分配指在Java被编译时就已经能够确定需要的内存空间,当程序加载时系统把内存一次性分配给它。这些内存只有在程序结束时才被收回。
    动态内存分配和回收
    Java中对象的内存空间是动态分配的,就是在程序执行时才知道要分配的存储空间大小,只有等到对象不再使用时才会被回收。
    如何检测垃圾
    只要某个对象不再被其他活动对象引用,那么这个对象就可以被回收。活动对象指能够被一个根对象集合到达的对象。
    根对象集合:
    1、方法中局部变量区中对象的引用
    2、Java操作栈中的对象引用
    3、常量池中对象引用
    4、本地方法中持有的对象引用
    5、类的Class对象
    基于分代的垃圾收集算法
    1. Young区 分为Eden区和两个Survivor区,其中新创建的对象都在Eden区,当Eden区满后触发minor GC 将Eden区仍然存活的对象复制到Survivor区中,另外一个Survivor区中的存活对象也复制到这个Survivor中,保证始终有一个Survivor区是空的。
    2. Old区存放的是Young区的Survivor满后触发minno GC后仍然存活的对象,当Eden区满后将对象存放到Survivor区中,如果Survivor中仍然存放不下这些对象,GC收集器会将这些对象直接存放到Old区。如果Survivor区中对象足够老,也直接存放到Old区。如果Old区也满了,将会触发Full GC回收整个堆内存。
    3. Perm区存放的主要是类的Class对象,如果一个类被频繁地加载,也可能会导致Perm区满,Perm区的垃圾回收也是Full GC触发的。
    三类垃圾收集算法:
    1、Serial Collector
    是JVM在client模式下的默认的GC方式,通过JVM配置参数-XX:+UseSerialGC来指定GC使用该手机算法。当Eden空间不足时就触发Minor GC,触发Minor GC时首先检查之前每次Minor GC时晋升到Old区的平均对象大小是否大于Old区的剩余空间,如果大于,则直接触发Full GC,如果小于,则看HandlePromotionFailur参数的值。如果为true,仅触发Minor GC,否则再触发一次Full GC。
    Java参数:java -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails
    2、Parallel Collector
    Parallel GC根据Minor GC 和Full GC的不同分为三种,分别是ParNewGC、ParallelGC和ParallelOldGC。
    1)ParNewGC:通过-XX:+UseParNewGC参数来指定,对象分配和回收策略与Serial Collector类似,只是回收的线程是多线程的。
    2)ParallelGC:Server下默认的GC方式,当在Eden区申请内存空间时,如果Eden区不够,那么看当前申请的空间是否大于等于Eden的一半,如果大于则这次申请的空间直接在Old中分配,小于则触发Minor GC。在触发GC之前首先会检查每次晋升到Old区的平均大小是否大于Old区的剩余空间,如果大于则再出发Full GC。在这次触发GC后仍然会按照这个规则重新检查一次。
    JVM参数:java -Xms20M -Xmx20M -Xmn10M -XX:+UsePaallelGC -XX:+PrintGCDetails
    3)ParallelOldGC
    和ParallelGC的区别:前者Full GC进行的动作为清空整个Heap堆中的垃圾对象,清楚Perm区中已经被卸载的类信息,并进行压缩。而后者是清楚Heap堆中部分垃圾对象,并进行部分的空间压缩。
    3、CMS Collector
    触发规则:检查Old区或者Perm区的使用率,当达到一定比例时触发CMS GC,触发时会回收Old区中的内存空间。触发Full GC:1)Eden分配失败,Minor GC后分配到To Space,To
    Space不够再分配到Old区,Old区不够再出发Full GC 2)当CMS GC正在进行时向Old申请内存失败则会直接触发Full GC。
    4、三种GC优缺点对比
    • 8.7 内存问题分析
    GC日志分析
    <collector>GC 表示收集器的名称
    <starting occupancy1>表示Young区在GC前占用的内存。
    <ending occupancy1>表示Young区在GC后占用的内存。
    <pause time1>表示Young区局部收集时JVM暂停处理的时间。
    <starting occupany2>表示JVM Heap在GC前占用的内存
    <ending occupany2>表示JVM Heap在GC后占用的内存
    <pause time2>表示GC过程中JVM暂停处理的总时间
    根据日志判断是否存在内存泄漏,如果<ending occupancy1>-<starting occupany1>=<ending occupancy2>-<starting occupany2>,则表明这次GC对象100%被回收,没有对象进入Old区或者Perm区。如果是大于号则这次回收对象进入Old区或者Perm区。如果<ending occupany2>一直增长,而且Full GC很频繁,则可能内存泄漏。

  • 相关阅读:
    k8s--容器挂载 error: /proc must be mounted
    mysql--read only
    C#读取Excel文件(.xls .xlsx)
    如何使用BBCode
    markdown使用经验积累
    openlayers学习之-----入门篇
    echarts学习之----动态排序柱状图
    echarts学习之----多图例折线图
    Web3D学习之-----全景图预览插件photo-sphere-viewer
    vue报错解决----npm ERR!
  • 原文地址:https://www.cnblogs.com/liguo-wang/p/9404866.html
Copyright © 2011-2022 走看看