电商网站建设行业现状,网站建设脱颖而出,百度打网站名称就显示 如何做,访问网站提示输入用户名密码java内存区域
1 Java 堆空间及 GC#xff1f;
首先我们要知道java堆空间的产生过程#xff1a; 即当通过java命令启动java进程的时候#xff0c;就会为它分配内存#xff0c;而分配内存的一部分就会用于创建堆空间#xff0c;而当程序中创建对象的时候 就会从堆空间来分…java内存区域
1 Java 堆空间及 GC
首先我们要知道java堆空间的产生过程 即当通过java命令启动java进程的时候就会为它分配内存而分配内存的一部分就会用于创建堆空间而当程序中创建对象的时候 就会从堆空间来分配内存所以堆空间存放的主要是对象和数组 而GC 其实说白了就是java虚拟机回收对象的机制即回收无效对象的内存用于将来的分配。
2 JVM 的主要组成部分及其作用
JVM包含两个子系统和两个组件两个子系统为Class loader(类装载)、Execution engine(执行引擎)两个组件为Runtime data area(运行时数据区)、Native Interface(本地接口)。 那么他们分别有啥作用呢 首先看Class loader根据给定的全限定名类名(如java.lang.Object)来装载class文件到Runtime data area中的method area。其实说白了就是类加载器简单点-作用就是通过类加载器将编译好的class文件加载到运行时数据区 Execution engine(执行引擎): 执行class中的指令 Native Interface(本地接口)与native libraries交互是其它编程语言交互的接口。 Runtime data area(运行时数据区域)这就是我们常说的JVM的内存。
整体的过程首先会通过编译器把Java代码转换成字节码源文件之后类加载器会把字节码文件加载到内存中即加载到运行时数据区 但是其实字节码文件只是JVM的一套指令集规范并不能直接交给底层操作系统来去执行因此需要特定的命令解析器即执行引擎将字节码翻译成底层的系统指令再交由CPU去执行。 同时java代码中 可以调用其他语言的本地库接口进行一些系统调用或者c函数的调用
3 说说JVM 运行时数据区 或说一下JVM内存模型
jvm内存模型大致被划分为如下几个区域 程序计数器也就是当前线程所执行的字节码指令的行号指示器说白了就是当前字节码执行到的位置字节码解析器的工作是通过改变这个计数器的值来选取下一条需要执行的字节码指令
java虚拟机栈用于存储方法执行时的局部变量表、操作数栈、动态链接、方法出口等信息也是线程私有的。每个方法在执行时都会创建一个栈帧栈帧包含了方法的局部变量表、操作数栈等信息。
本地方法栈与 Java 虚拟机栈类似只不过虚拟机栈是服务 Java 方法的而本地方法栈是为虚拟机调用 Native 方法服务的
堆用于存储对象实例和数组它也是jvm中最大的一块内存区域同时也是线程共享的而堆包括年轻代和老年代以支持垃圾回收机制。
年轻代Young Generation用于存放新创建的对象。年轻代又分为 Eden 区和两个 Survivor 区通常是一个 From 区和一个 To 区对象首先被分配在 Eden 区经过垃圾回收后存活的对象会被移到 Survivor 区经过多次回收后仍然存活的对象会晋升到老年代。老年代Old Generation用于存放存活时间较长的对象。老年代主要存放长时间存活的对象或从年轻代晋升过来的对象。
方法区Methed Area用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。
4 什么是堆内存?
以Hotspot为例堆内存主要由GC模块进行管理和分配可以分为新生代和老年代而新生代又可以分为eden区s1区和s2区并且他们的比例默认为8:1:1, 同时新生代和老年代的占比如下图所示 在使用堆内内存on-heap memory的时候完全遵守JVM虚拟机的内存管理机制采用垃圾回收器GC统一进行内存管理 GC会在某些特定的时间点进行一次彻底回收也就是Full GCGC会对所有分配的堆内内存进行扫描在这个过程中会对JAVA应用程序的性能造成一定影响还可能会产生Stop The World。
5 什么是非堆内存
通常来说方法区是非堆内存而jdk1.8之前的方法区实现是永久代而jdk1.8之后的方法区实现叫元空间
6 什么是堆外内存呢
堆外内存, 常常又叫做直接内存。 和堆内内存相对应堆外内存就是把内存对象分配在Java虚拟机的堆以外的内存 这些内存直接受操作系统管理而不是虚拟机这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。 具体实现 可以 用java.nio.DirectByteBuffer对象进行堆外内存的管理和使用 那么堆外内存的优点是什么 减少垃圾回收因为垃圾回收会暂停其他的工作。 加快复制速度堆内在flush到远程时会先复制到直接内存非堆内存然后在发送而堆外内存相当于省略掉了这个工作。
7 永久代和方法区又有什么区别呢
方法区是《Java虚拟机规范》中定义的内存区域用于存储类的结构信息如类的字节码、常量池、字段和方法信息等 而 Java 默认虚拟机 HotSpot 中在 JDK 1.8 之前的版本中是通过永久代来实现方法区的但 JDK 1.8 之后永久代被元空间Metaspace取代。 所以总结来看方法区是规范而永久代和元空间是具体实现。
8 那么为啥用元空间来取代永久代呢
主要原因有以下几点
兼容JRockit --首先java官方要收购JRocket而JRockit 中没有“永久代”的概念 所以为了更好的融合JRockit和Hotspot源码因此取消了永久代。其次可以提高稳定性降低OOM的风险-因为对于永久代来说是需要设置PermSize 和 MaxPermSize 参数的而一旦这两个值设置的不合理或者设置的过小就会频繁触发FullGC 和导致 OOMOut of Memory内存溢出,但是当使用元空间来替代永久代后 出现OOM的风险就大大降低了因为元空间使用的是本地内存也就是只和本地内存大小有关。降低运维成本–因为用了元空间后就不在需要运维或者开发人员手动的来去设置和调整元空间的大小
9 为什么调整字符串常量池的位置
其实主要是为了提高回收效率便于及时的回收常量池内存同时也是会缓解永久代空间不足的问题 首先我们要知道字符串常量池在1.7之前是放到永久代的而永久代的回收效率是很低的只有在fullGC的时候才会触发而fullGC的触发也是需要再老年代的空间不足或者永久代不足时才会进行触发这样就导致了字符串常量池的回收效率并不高。但其实在真正的开发中我们一般会有大量的字符串常量被创建回收效率低反过来也会导致永久代空间不足 因此在jdk1.7之后字符串常量池被放到了堆空间中便于及时的回收内存
10成员变量、局部变量、类变量分别存储在内存的什么地方 类变量 类变量是用static修饰符修饰定义在方法外的变量随着java进程产生和销毁。在java8之前把静态变量存放于方法区在java8时存放在堆中 成员变量 成员变量是定义在类中但是没有static修饰符修饰的变量随着类的实例产生和销毁是类实例的一部分 由于是实例的一部分在类初始化的时候从运行时常量池取出直接引用或者值与初始化的对象一起放入堆中 局部变量 局部变量是定义在类的方法中的变量 在所在方法被调用时放入虚拟机栈的栈帧中方法执行结束后从虚拟机栈中弹出所以存放在虚拟机栈中
11 堆和栈有什么区别
具体区别可以看以下几点 1 存放的内容 堆存放的是对象的实例和数组而栈存放的是局部变量操作数栈 2 再从是否私有来看 堆对于整个应用程序来说都是共享的而栈是线程独享所以也是线程私有。他的生命周期和线程相同 3 物理结构 堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配所以有各种算法。 栈使用的是数据结构中的栈先进后出的原则物理地址分配是连续的。所以性能快。
12 什么是双亲委派模式
说到双亲委派模式 我们先聊下什么是类加载器 以及类加载器的分类 类加载器Class Loader是 Java 虚拟机JVM的重要组成部分负责将字节码文件加载到内存中并转换为可执行的类 而类加载器大致可以分为四种 启动类加载器加载 JDK 中 lib 目录中 Java 的核心类库即$JAVA_HOME/lib目录。 扩展类加载器加载 lib/ext 目录下的类 应用程序类加载器加载我们写的应用程序 自定义类加载器根据自己的需求定制类加载器。
而 双亲委派模型是 Java 类加载器的一种工作机制。 它是指当一个类加载器需要加载一个类时它首先不会自己去尝试加载这个类而是把这个请求委派给父类加载器去完成每一个层次的类加载器都是如此因此所有的加载请求最 终都应该传送到最顶层的启动类加载器中只有当父加载器反馈自己无 法完成这个加载请求它的搜索范围中没有找到所需的类时子加载器才会尝试自己去完成加载。 双亲委派模型的优点是啥 避免重复加载类比如 A 类和 B 类都有一个父类 C 类那么当 A 启动时就会将 C 类加载起来那么在 B 类进行加载时就不需要在重复加载 C 类了。 提高安全性使用双亲委派模型也可以保证了 Java 的核心 API 不被篡改如果没有使用双亲委派模型而是每个类加载器加载自己的话就会出现一些问题比如我们编写一个称为 java.lang.Object 类的话那么程序运行的时候系统就会出现多个不同的 Object 类而有些 Object 类又是用户自己提供的因此安全性就不能得到保证了。
那么如何打破双亲委派机制呢 1.自定义类加载重写loadclass方法 因为双亲委派的机制都是通过这个方法实现的这个方法可以指定类通过什么类加载器来进行加载所有如果改写他的加载规则相当于打破双亲委派机制
2.使用线程上下文类 双亲委派模型的第二次“破坏”是由这个模型自身的缺陷所导致的双亲委派很好的解决了各个类加载器的基础类统一问题基础类之所以“基础”是因为他们总被用户代码所调用但是如果基础类又要重新调用用户代码那咋办 比如说JNDI是java的标准服务它的代码是由启动类加载器进行加载的但是jndi的作用就是进行资源的集中管理和查找它需要调用由开发人员开发在classpath下的类代码但是启动类加载器不会进行加载。 所以引入线程上下类加载器通过java.lang.Thread类的setContextClassLoader()方法进行设置。如果创建线程是还未设置它会从父线程继承一个如果在应用程序全局范围内没有设置那么这个线程上下类加载器就是应用程序类加载器。 那么这样JNDI服务使用这个线程上下类加载器去加载所需的spi代码也就是父类加载器请求子类加载器去完成类加载的动作这个实际是打通了双亲委派的逆向层次结构。