网站建设管理职责,新乡网站推广公司,花瓣网网站模板,北京网站开发教师招聘java虚拟机 java内存区域 jvm的主要组成部分及作用 主要包含两个子系统和两个组件 子系统 类加载器#xff08;Class loader#xff09;#xff1a;根据给定的类路径来装载class文件到运行时数据区 …java虚拟机 java内存区域 jvm的主要组成部分及作用 主要包含两个子系统和两个组件 子系统 类加载器Class loader根据给定的类路径来装载class文件到运行时数据区 执行引擎Execution engine执行class中的指令 组件 运行时数据区runtime data areajvm内存 本地接口native interface 与本地接口库进行交互与其他语言交互的接口 作用通过编译器把java代码转换成字节码类加载器再把字节码加载到内存中将其放在运行时数据区的方法区中而字节码文件只是jvm的一套指令集规范并不能直接交给底层操作系统去执行因此需要特定的命令解析器执行引擎将字节码翻译成底层系统指令在交由cpu执行而这个过程中需要调用其他语言的本地接口来实现整个程序的功能 java程序运行机制步骤 首先编写java源码源文件.java 在利用编译器将源码编译成字节码文件。.class 运行字节码文件是通过解释器java命令来完成 jvm运行时数据区 程序计算器program counter register当前程序所执行的字节码的行号指示器。字节码解析器的工作是通过改变这个计数器的值来选取下一次需要执行的字节码指令 栈java virtual Machine stacks用于存储局部变量表操作数栈动态链接方法出口等信息 本地方法栈native method stack为虚拟机调用本地方法服务的 堆java heap虚拟机内存最大的一块是被所有线程共享的几乎所有的对象实例都在这里分配内存 方法区method area用户存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据 深拷贝和浅拷贝 浅拷贝 只是增加了一个指针指向已存在的内存地址 深拷贝 增加了一个指针并且申请了一块新内存使新增的指针指向新内存 浅复制 指向被复制的内存地址如果原地址发生改变那么复制出来的对象也会相应改变 深复制 开辟一块新的内存地址存放被复制的对象 堆栈的区别 物理地址 堆的物理地址分配对对象是不联系的。因此性能慢些在gc的时候也要考虑到不连续的分配所以由各种算法。比如标记清楚、复制、标记压缩、分代新生代用复制老年代用压缩 栈使用的是数据结构的中的栈先进后出的原则物理地址分配的是连续的所以性能快 内存分别 堆因为是不连续的所以分配内存是在运行时确认的因此大小不固定一般堆大小远远大于栈 栈是连续的所以分配的大小要在编译期就确定大小是固定的 存放的内容 堆存放的是对象的实例和数组 栈存放局部变量操作数栈返回结果 程序的可见性 堆对于整个应用程序都是可见、共享的 栈只对于线程是可见的所以线程也是私有的他的生命周期和线程相同 队列和栈是什么有什么区别 队列和栈都是用来存储数据的 区别 操作名称不同 队列的插入称为入队删除称为出队栈的插入称为入栈删除称为出栈 可操作的方式不同 队列是在尾部入队头部出队两边都可操作。栈的出栈、出栈都是在栈顶进行的 操作的方法不同 队列是FIFO先进先出栈是LIFO后进先出 hotspot虚拟机对象 对象的创建 创建对象的几种方式 使用new关键字 调用构造函数 使用Class的newInstance方法 调用构造函数 使用Constructor类的newInstance方法 调用构造函数 使用clone方法 没有调用构造函数 使用反序列化 没有调用构造函数 创建对象的流程 类加载检查 虚拟机收到一个new的指令new 关键字、clone、对象序列化时首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用并且检查这个符号引用代表的类是否被加载、解析、初始化过如果没有就需要执行相应的类加载过程 分配内存 类加载检查通过后虚拟机会为新生对象分配内存对象所需内存的大小在类加载完成后便可完全确定为对象分配的空间任务等同于把一块确定大小的内存从java堆中划分出来 划分内存的方法 指针碰撞Bump the pointer默认使用 如果堆中的内存是规整的所有用过的内存都放在一边空闲的放一边中间放着一个指针作为分界点的指示器分配空间就是把那个指针网·向空闲空间那边挪动一段与对象大小相等的距离 空闲列表free list 如果堆中的内存不是规整的已使用的内存和空闲内存相互交错那就没办法简单的指针碰撞虚拟机必须维护一个列表记录哪些内存块是可用的再分配的时候从列表中找到一块足够大的空间划分给对象实例并更新列表上的记录 解决并发问题 cas 采用cas配上失败重试的方式保证更新操作的原子性来分配内存空间的动作进行同步出咯 本地线程分配缓冲(TLAB) 把内存分配的动作按照线程划分在不同的空间之中进行 初始化零值 内存分配完后虚拟机要将分配到的内存空间都初始化为0值不包括对象头这一步保证了对象的实例字段在java代码中可不赋初始值就可以直接使用 设置对象头 初始化零值后虚拟机要对对象进行必要的设置如对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象gc的分代年龄等信息这个信息存放在对象头中 在Hotspot虚拟机中对象在内存中存储的布局可以分为三个区域对象头、实例数据、对齐填充 对象头包括两部分信息第一部分用于存储对象自身运行时数据如哈希码、gc分代年龄、锁标志、线程持有的锁、偏向线程id、偏向时间戳等。另一部分是他的类元数据指针即对象指向它的类元数据指针虚拟机通过这个指针来确定这个对象是哪个类的实例 执行init方法 对象按照程序猿的意愿进行初始化就是为属性赋值和执行构造方法。 对象的访问定位 java程序需要通过jvm栈上的引用访问堆中的具体对象。目前主流的访问方式有句柄和指针 句柄访问【堆中分出一块内存作为句柄池引用中存储对象的句柄地址而句柄中包含了对象实例数据与对象类型数据各自的具体地址信息】 优势引用存储的是稳定的句柄地址在对象被移动时智慧改变句柄中的实例数据指针而引用本身不需要修改 指针访问【引用中存储的直接就是对象地址】 优势速度更快节省了一次指针定位的时间开销hotspot使用的就是这种 内存溢出异常 java会存在内存泄漏吗请简单描述 存在 长生命周期的对象持有短声音周期对象的引用就可能导致内存泄漏 垃圾收集器 简述java垃圾回收机制 在java中程序员是不需要主动的去释放一个对象的内存。而是由虚拟机自行执行在jvm中有一个垃圾回收线程他是低优先级的在正常情况下是不会执行的只有在虚拟机空闲或者当前堆内存不足时才会触发执行扫描哪些没有被任何引用的对象并将它们添加到要回收的集合中进行回收 GC是什么为什么需要GC gc是垃圾收集 为什么需要GC保证程序能够正常运行的条件随着程序的运行可用的内存就会越来越少GC不可避免同时还要对内存碎片进行管理 垃圾回收的优点和原理并考虑两种回收机制 编程时程序员可以不用考虑内存的问题可以有效的放置内存泄漏有效的使用可使用的内存 垃圾回收机制分代复制垃圾回收、标量垃圾回收、增量垃圾回收 垃圾回收的基本原理是什么垃圾回收器可以马上回收内存吗有什么办法主动通知虚拟机进行垃圾回收 当程序员擦行间对象时gc就开始监控这个对象的地址、大小不急使用情况 通常GC采用有向图的方式记录和管理堆中的所有对象通过这种方式确定哪些对象是“可达的”哪些对象是“不可达”当gc确定一些对象为不可达时gc就有责任回收这些内存 可以手动调用system.gc()通知gc回收但是java语言规范并不保证gc一定会执行 Java中有哪些引用类型 强引用发生gc时不会回收 软引用有用但不是必须的对象在发生内存溢出之前会被回收 弱引用有用但不是必须的对象在 下次GC时会被回收 虚引用用途是在gc时返回一个通知 怎么判断对象是否可以被回收 引用计数法 为每个对象创建一个计数器被引用1释放引用-1当计数器为0时就会被回收他有一个缺点不能解决循环引用的问题 可达性分析 从GC Root线程栈的本地变量、静态变量、本地方法栈等开始向下搜索引用的对象找到的对象被标记为非垃圾对象其余未被标记的都视为垃圾对象。 在java中对象什么时候可以被垃圾回收 设置为null 没有被其他线程对象引用 JVm中永久代中会发生垃圾回收吗 gc。这时候永久代是会被回收的这也就是为什么正确的永久代大小对避免full gc是非常重要的原因 说一下JVM有哪些垃圾回收算法 标记清除 标记无用对象然后进行回收。缺点效率不高无法清除垃圾碎片 复制算法 按照容量划分2个大小一样的内存区域当一块用完的时候将存货的对象复制到另一块内存中然后把已使用的内存一次清理掉。缺点:内存使用率不高只有原来的一半 标记整理 标记无用对象将存活的对象向一端移动然后直接清除端边界以外的内存 分代算法 将对象存货周期的不同将内存划分几块一般是新生代复制算法、老年代标记整理算法和永久代 说一下JVM有哪些垃圾回收器 如果说垃圾回收算法是内存回收的方法论那么垃圾收集器就是内存回收的具体实现。 用户回收新生代的回收器有Serial、PraNew、Parallel Scavenge回收老年代的回收器有Serial Old、Parallel Old、CMS还有用于回收整个Java堆的G1回收器。不同的回收器之间可以搭配使用 serial复制算法新生代单线程收集器标记和清理都是单线程有点是简单高效 PraNew复制算法新生代并行收集器实际上是serial的多线程版本在多核cpu环境下有着比serial更好的表现 Parallel Scavenge复制算法新生代并行收集器和parNew差不多区别主要在于parNew可以喝CMS配合使用 serial old 标记整理算法老年代单线程收集器serial的老年代版本 parallel old标记整理算法老年代并行收集器吞吐量优先parallel scavenge的老年代版本 CMSConcurrent Mark Sweep标记清楚算法老年代并行收集器以获取最短回收停顿为目标的收集器具有高并发低停顿的特点追求最短的gc回收停顿时间 G1Garbage First收集器标记整理算法:Java堆并行收集器【g1的回收范围是整个堆包括新生代和老年代而上面六个回收的范围仅限于老年代或者新生代 详细介绍一下CMS垃圾回收器 CMSConcurrent Mark sweep是以牺牲吞吐量为代价来获得最短的回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上这种垃圾回收器非常适合。 在JVM参数上加上-XX:UseConcMarkSweepGC指定使用 CMS使用的是标记清除算法所以在gc的时候会产生大量的内存碎片当剩余内存不足以满足程序运行要求是系统会出现“Concurrent mode Failure”,此时进入stop the world用serial old垃圾回收器进行回收 新生代垃圾回收器和老年代回收器都有哪些有什么区别 新生代 serial、parNew、Parallel scavenge 老年代 serial old、parallel old 、CMS 整堆回收 G1 新生代采用的复制算法优点是效率高缺点是内存利用率低 老年代一般用的都是标记整理算法 简述一下分代垃圾回收器是怎么工作的 分代回收器有两个分区老年代和新生代新生代默认的空间占总空间的1/3老年代是2/3 新生代使用的是复制算法有三个分区eden、to Survivor、from Survivor他们的默认占比是8:1:1.执行流程如下 把edenfrom survivor存活的对象放入 to survivor区 清空 eden和from survivor分区 from survivor和to survivor分区相互交换 每次from survivor到to survivor移动时都存活的对象年龄就1当年龄到达15默认配置15时就升级到老年代大对象也会进入老年代 老年代当空间占用到达某个值后就会触发全局垃圾回收一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体流程 内存分配策略 简述java内存分配与回收策略以及minor gc和major gc 对象内存的分配通常是在堆上分配随着虚拟机的优化技术诞生某些场景下也会在栈上分配对象主要分配在新生代的eden区如果启用了本地线程缓冲TLAB),将按照线程优先在TLAB上分配少数情况下也会直接在老年代上分配总的来说分配规则不是百分百固定的其细节处决于哪一种垃圾收集器组合以及虚拟机相关参数 对象优先在eden区分配 多数情况下对象都在新生代eden区进行分配。当eden区分配没有足够的空间时虚拟机将会发起一次minor gc如果本次gc后还是内存还是不够将启用老年代空间分配担保机制在老年代中分配。 mainor gc是指发生在新生代的gc因为java对象大多数是朝生夕死所以mainor gc非常频繁一般回收速度也很快 major gc/Full gc是指发生在老年代的gc出现major gc通常会伴随着一次minor gc。major gc的速度通常会比minor gc慢10倍以上 大对象直接进入老年代 所谓的大对象是指需要大量连续内存空间地址的对象频繁出现大对象是致命的会导致内存中还有不少空间的情况下提前触发GC以获取足够的连续空间来安置新对象 因为新生代是使用复制算法来处理回收如果大对象直接在新生代分配就会导致eden区和两个survivor区之间发生大量的复制。因此大对象都会在老年代进行分配 长期存活对象将进入老年代 虚拟机给每个对象定义了一个年龄计数器如果对象在eden区中出生并且能够被survivor容纳将被移动到survivor空间中这是设置对象的年龄为1对象在survivor区中每minorgc一次年龄就1.当达到一定值后默认15就会被晋升为老年代 类加载机制 简述java类加载机制 虚拟机把描述类的数据从Clas文件中加载到内存并对数据进行校验解析和初始化最终形成可以被虚拟机直接使用的java类对象 描述一下JVM加载Class文件的原理机制 java中的所有类都需要类加载器装载到JVM中才能运行。类加载器本身也是一个类而它的工作就是把Class文件从硬盘读取到内存中。在写程序的时候我们继续不需要关心累的加载因为这些都是隐式装载的除非我们有特殊的用法像是反射我们就需要显式的加载所需的类 类装载的方式两种 隐式装载 在程序运行过程中通过new等方式生成对象时隐式调用类装载器架子啊对应的类到JVM中 显式装载 通过class.forNamed()等方法显式加载需要的类 什么是类加载器类加载器有哪些 通过类的权限定义名获取该类的二进制字节流的代码叫做类加载器 四类加载器 启动类加载器 负责加载支撑jvm运行的位于jre的lib目录下的核心类库如rt.jar 扩展类加载器 负责加载支持jvm运行的位于jre的lib目录下的ext扩展目录中的jar类包 应用程序加载类系统加载类 负责加载classPath路径下的类包主要就是加载自己写的类 自定义加载器 负责加载用户自定义路径下的类包 说一下类加载器的执行过程 类加载分为5个步骤 加载在硬盘上查找并通过io读入字节码文件使用到的类才会加载 验证校验字节码文件的正确性 准备给类的静态变量分配内存、并赋予默认值 解析将符号引用替换为直接引用。该阶段会把一些静态方法符号引用替换为指向数据所在内存的指针或者句柄直接引用这就是所谓的静态链接动态链接是指在程序运行期间完成的符号引用替换直接引用 初始化对类的静态变量初始化为指定的值执行静态代码块 什么是双亲委派模型 加载某个类时会先委托父加载器寻找目标类找不到再委托上层父加载器加载如果所有的父加载器都找不到则在自己的类加载路径中查找并载入类 简单说就是先找父亲加载是在不行再有儿子加载。 为什么要设计双亲委派机制 沙箱安全机制 自己写的String类不会被加载这样可以防止核心api被随意篡改 避免类的重复加载 当父类已经加载了该类就没有必要子加载器在加载一次保证被加载类的唯一性 java类加载是动态的它并不会一次性将所有的类全部加载后运行而是加载能保证程序运行的基础类至于其他类则是在需要的时候才加载为了节省内存开销 JVM调优 调优工具 阿里的Arthas Jdk自带的jconsole和jvisualVm JVM调优参数都有哪些 • -Xms2g初始化推大小为 2g • -Xmx2g堆最大内存为 2g • -XX:NewRatio4设置年轻的和老年代的内存比例为 1:4 • -XX:SurvivorRatio8设置新生代 Eden 和 Survivor 比例为 8:2 • –XX:UseParNewGC指定使用 ParNew Serial Old 垃圾回收器组合 • -XX:UseParallelOldGC指定使用 ParNew ParNew Old 垃圾回收器组合 • -XX:UseConcMarkSweepGC指定使用 CMS Serial Old 垃圾回收器组合 • -XX:PrintGC开启打印 gc 信息 • -XX:PrintGCDetails打印 gc 详细信息。