当前位置: 首页 > news >正文

大气的门户网站软件开发外包网站

大气的门户网站,软件开发外包网站,wordpress 怎么设置主页,南昌网页制作公司巩固基础#xff0c;砥砺前行 。 只有不断重复#xff0c;才能做到超越自己。 能坚持把简单的事情做到极致#xff0c;也是不容易的。 JVM 类加载机制 JVM 类加载机制分为五个部分#xff1a;加载#xff0c;验证#xff0c;准备#xff0c;解析#xff0c;初始化砥砺前行 。 只有不断重复才能做到超越自己。 能坚持把简单的事情做到极致也是不容易的。 JVM 类加载机制 JVM 类加载机制分为五个部分加载验证准备解析初始化下面我们就分别来看一下这五个过程 加载 加载是类加载过程中的一个阶段这个阶段会在内存中生成一个代表这个类的java.lang.Class 对象作为方法区这个类的各种数据的入口。注意这里不一定非得要从一个Class 文件获取这里既可以从 ZIP 包中读取比如从jar 包和 war 包中读取也可以在运行时计算生成动态代理 也可以由其它文件生成比如将 JSP 文件转换成对应的Class 类 验证 这一阶段的主要目的是为了确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求并且不会危害虚拟机自身的安全 准备 准备阶段是正式为类变量分配内存并设置类变量的初始值阶段即在方法区中分配这些变量所使用的内存空间 解析 解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程 符号引用 符号引用与虚拟机实现的布局无关引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同但是它们能接受的符号引用必须是一致的因为符号引用的字面量形式明确定义在 Java 虚拟机规范的Class 文件格式中 直接引用 直接引用可以是指向目标的指针相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用那引用的目标必定已经在内存中存在 初始化 初始化阶段是类加载最后一个阶段前面的类加载阶段之后除了在加载阶段可以自定义类加载器以外其它操作都由JVM 主导。到了初始阶段才开始真正执行类中定义的Java 程序代码 类构造器 初始化阶段是执行类构造器方法的过程。方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证子方法执行之前父类的方法已经执行完毕如果一个类中没有对静态变量赋值也没有静态语句块那么编译器可以不为这个类生成()方法。 注意以下几种情况不会执行类初始化 通过子类引用父类的静态字段只会触发父类的初始化而不会触发子类的初始化。定义对象数组不会触发该类的初始化常量在编译期间会存入调用类的常量池中本质上并没有直接引用定义常量的类不会触发定义常量所在的类通过类名获取Class 对象不会触发类的初始化。通过Class.forName 加载指定类时如果指定参数 initialize 为false 时也不会触发类初始化其实这个参数是告诉虚拟机是否要对类进行初始化。 通过ClassLoader 默认的loadClass 方法也不会触发初始化动作。 虚拟机类加载机制类加载过程 加载过程 接下来我们详细讲解一下加我群里一中类加载的全过程也就是加载验证准备解析和初始化这五个阶段所执行的具体动作。 加载 加载是类加载过程的一个阶段希望读者没有混淆。这两个看起来好像是的名词在家找阶段虚拟机需要完成下面三件事。 1.通过一个类的全限定名来获取定义此类的二进制字节流。 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。 3.在内存中生成一个代表这个类。的对象作为方法区这个类的各种数据的访问入口 虚拟机规范的这三点要求其实并不算具体因此虚拟机实现与具体应用的灵活度。都是相当大的例如通过一个类的全限定名来。获取定义此类的二进制字节流这条它并没有指定二进制字节流。一定要从一个class文件中获取准确的说是根本没有指定从哪里获取怎样获取。虚拟机设计团队在加载阶段。搭建了一个相当开放广阔的舞台Java发展历程中充分创造力的开发人员则在这个舞台上玩出了各种花样。举足轻重的Java技术都建立在这一基础之上例如 1从zip包中读取这种很常见最终日后的炸ear。war格式的基础 2从网络中获取这种场景最典型的就是applied 3运行时计算生成这种场景使用的最多的就是动态代理技术。在java.lang.reflect.proxy中就是使用了Process generator店generator proxy class。来为特定接口生成显示为$ proxy代理类的二进制字节流。 4有其他文件产生。典型的应用就是JSP应用既有这次文件上传对应的class类。 5从数据库中读取这种场景相对少见例如有些中间件服务器。可以选择把应用程序安装到数据库中来完成程序代码。在集群间的发放。 相当于类加载过程的其他阶段一个非数组类的加载阶段准确的说是加载阶段中获取类的二进制。自己留的动作是开发人员可控性最强的因为加载阶段既可以使用系统提供的引导类加载器来完成也可以由用户自定义的类加载器去完成。开发人员可以通过自定义的类加载器去控制自己留的获取方式。G重写一个类加载器的load class方法。 对数组类而言情况就有所不同数组类本身不通过类加载器创建它是由加瓦虚拟机直接创建的但数组类与类加载器仍然有很密切的关系因为数组类的元素类型。指的是数组去掉所有维度的类型最终是要考虑加载器去创建。一个数组类创建过程就要遵循以下原则。 1如果数组的组件类型是引用类型那就要递归采用绑结。中定义的加载过程去加载这个组件类型。数组c将在加载该组建类型的类加载器的。那名空间上被标识 2如果数组的组件类型不是引用类型千瓦虚拟监会把数组c标记为与引导类加载器关联。 3数组类型的可见心与他的组件类型的可见性一致如果组件类型不是引用类型那出组的。类的可见性被默认为public。 加载阶段完成好后虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中。方法句中的数据存储格式由虚拟机实现自新定义。胸肌规范未规定此区域的具体数据结构然后在内存中实例化一个java.lang.class类的对象。并没有明确规定是在Java堆中。对于hot stop虚拟机而言Class对象比较特殊它虽然是对象但是存储在方法区里面。这个对象将作为程序访问方法区中的这些类型数据的外部接口。加载阶段与链接阶段的部分内容是交叉进行的。加载阶段尚未完成链接阶段可能已经开始但这些加载加载阶段之中进行的动作仍然属于链接阶段的内容这两个阶段的开始时间仍然保持着固定的先后顺序。 虚拟机的加载机制验证 验证试验阶段的第一步这一步的目的是为了确保class文件的字节流包含的信息符合当前虚拟机的要求。而且不会危害虚拟机自身的安全 java语言本身是相对安全的语言使用纯粹加瓦语言的代码。无法做到。诸如如何访问数组边界以外的数据将一个对象转化为它。并为实现的类型跳转到不存在的代码行之类的事情。如果这样做了边界将拒绝便宜但前面杰说过class文件并不一定要求Java源码编译而来。可以使用任何途径产生甚至包含用16进制编辑器直接编写下来的class文件。再次解码语言层面上删除Java代码无法做到的事情是可以实现的至少遇上。可以表达出来的虚拟机如果不检查输入的字节流对其完全信任的话很可能因为载入有害的字节流而导致系统崩溃所以验证是虚拟机对自身保护的一项重要工作 验证阶段是非常重要的这个阶段是否严谨直接决定了交往虚拟机是否传授恶意代码攻击从执行性能的角度讲讲验证阶段的工作量是虚拟机的类加载此系统中账了相当大的一部分。对这个机制的限制知道还比较笼统的规范中列举了一些class文件格式的静态和结构化约束如果验证到输入的字节流不符合class文件格式的约束虚拟机就会抛出一个Java。乱点verifyexception异常或此类异常但具体应当检查哪些方面如何检查核实检查都没有足够的要求和明确的说明直到2011年发布的Java虚拟机规范Java se第七版。大幅增加了描述验证过程的篇幅从不到十页增加到100。30页这时约束和验证规则才变得具体起来受篇幅所限。当初无法逐条规则去剪剪当从整体上去看验证阶段大致会完成下面四个阶段的检验动作。文件格式检验、元数据检验、字节码检验、符号引用检验。 文件格式检验 第一阶段要验证自己留是否符合class文件格式的规范并且能被当前版本的虚拟机处理这一阶段可能包含下面这些验证点。 1.是否以魔数xcafebabe开头 2.主次版本号是否在当前虚拟机处理范围之内。 3.常量值的常量中是否有不被支持的常量类型 4.指向常量的各种索引值中是否有只限不存在的常量或不符合类型的常量 5.Class文件中各个部分及文件本身是否有被删除或附加的其他信息。 实际上第一阶段的验证点远不止如此上面只是从hot stop虚拟机。源码中宅出的一小部分内容该验证阶段的主要目的是保证输入的字节流。蓝正确的解析并存储于方法区之内格式上符合描述一个Java类型信息的要求这阶段的验证是基于二进制字节流进行的。只有通过了这个阶段的验证后字节流才会进入内存的方法区中进行存储。所以后面的三个验证阶段全部是基于方法区的存储结构进行的。不会再直接操作字节流 元数据验证 第二阶段是对字节码描述的信息进行语义分析一保障其描述的信息符合Java。语言规范的要求这个阶段可能包含的验证点如下 1.这个类是否有父类除了object这个他之外所有的累都有父类。 2.这个类的父类是否继承了不允许被继承的类被final修饰的类 3.如果这个类不是抽象类是否实现了其父类或接口之中要求实现的所有方法 4.类中的字段方法是否与父类产生矛盾如果覆盖了父类的final字段。或者出现了不符合规则的方法重载例如方法参数都一致当返回类型却不相同等 第二阶段主要的目的是对类的元数据信息进行语义检验保证不存在不符合java语言规范的元数据信息 字节码验证 第三阶段是整个验证过程中最复杂的一个阶段。主要的目的是通过数据流和控制流分析确定程序语义是合法的符合逻辑的。在第二阶段对元数据信息中的数据类型做完校验后这个阶段间对类的方法体进行就业分析保障被救援的类的方法在运行时不会做出危险虚拟机的安全事件例如 1.保证任意时刻操作数栈的数据类型和指令编代码。序列都能配合工作例如不会出现类似的情况。在操作站放了一个int类型的数据使用时确按照long来加载到本地变量表中。 2.保证跳转指令不会跳转到方法体以外的字节码的指令上。 3.保障方法体重的类型状况是有效的例如可以把一个子类对象赋值给父类数据类型。这是安全的当时把父类对象赋值给此类数据类型甚至把对象赋值给它毫无继承关系完全不相干的一个数据类型。这是危险合不合法的 如果一个类方法体的字节码没有通过字节码验证那肯定是有问题的。但如果一个方法体通过了字节码验证也不能说明其一定就是安全的。即使自己码验证之中存在了大量的检查也不能保证这一点这里涉及那离散数学中一个很著名的问题Halting Problem。通俗一点说通过查数据校验程序逻辑是无法做到绝对准确的不能通过程序准确的检查出程序是否能在有限时间之内结束运行。 对于数据流验证的高复杂性迅疾设计团队为了避免过多的时间how在自己把验证阶段在这dk1.6之后的加我c编译器和Java虚拟机中进行了一键优化给封话题的code属性。属性表中增加了一下名为stick map table的属性。这项属性描述了方法体中所有的基本块儿。按照流程拆封的代码块开始本地变量表和操作数栈。亦友的状态再次解绑验证期间就不需要根据程序推导这个状态的合法性。只需要检查stick maple table属性中的记录是否合法即可。这样直接把验证的类型推导变成了类型检查从而节约了一些时间。 理论上的stick map table属性也存在错误或被篡改的可能。所以是否有可能在恶意篡改了code属性的同时也生成相应的stick map table属性来骗过迅疾的内心。校验则是虚拟机设计者值得思考的问题 在jdk1.6的hot spot报虚拟机提供了-XX: UseSplitverifer选项来关闭此优化或者使用参数。-XX: FailOverToOldVerfier要求在类型校验失败的时候退回到旧的类型推导方式进行校验。而在这第一个1.7之后对于主版本大于50的class文件使用类型检查来完成数据流分析。校验则是唯一的选择不允许再退回到类型推导的方式 符号引用验证 最后一个阶段的校验发生在迅即将符号引用转换为直接引用的时候这个转化动作间在链接的第三阶段解析阶段中发生。符号引用验证可以看做是对类自身以外的信息进行匹配性。检验。通常需要检验下列的内容 1.符号应用中通常字符串描述的全限地名是否找到对应的类。 2.在指定类中是否存在符号方法的字段描述符以及简单米长所描述的方法和字段。 3.符号引用中的类字段方法的访问c是否可以被当前类访问 符号引用验证的目的是保证解析动作正常执行如无法通过符号引用验证。那么将会抛出一个异常的子类。 对于虚拟机的类加载机制来说验证阶段是一个非常重要的但不是一定必要的阶段如果所运行的全部代码都已经被反复使用和验证过那么在实施阶段就可以考虑使用参数-Xverify: none仓鼠来关闭大部分的内件验措施以缩短虚拟机类加载的时间。 虚拟机类加载机制准备 准备阶段是正式为类变量分配内存空间。并设置类变量初始值的阶段这些变量所使用的内存将在方法区中进行分配这个阶段中有两个容易产生混淆的概念要强调一下首先这时候进行的内存分配仅包含类变量被static修饰的变量。而不包含实例变量实例变量将会在对象实例化时随着对象一起分配到甲瓦堆中。其次这里所说的初始化通常情况下是指数据类型的零值。 上面提到在通常情况下初始值是零值那么相对的会有一些特殊情况如果类字段的属性表中存在constant value属性。那么在准备阶段变量value就会被初始化为constant value属性所指定的值。如被final修饰。 虚拟机类加载机制初始化 虚拟机类加载机制 代码编译的结果从本地机器码转变为字节码是存储格式发展的一小步却是编程语言发展的一大步。 概述 上一章我们了解了class文件存储格式的具体细节在class文件中描述的各种信息。最终都需要加载到虚拟机中之后才能运行和使用而虚拟机如何加载这些class文件class文件中的信息进入到虚拟机后会发生什么变化这些都是本章要进行讲解的内容。 去你鸡巴描述类的数据从class文件加载到内存并对数据进行校验转换解析和初始化最终形成。可以被虚拟机直接使用的Java类型这就是虚拟机的类加载机制。 与那些在编译时需要进行链接工作的语言不同在Java语言里类型的加载。链接和初始化过程都是在程序运行期间完成的这种策略虽然会令那加载时稍微增加一些性能开销倒是会为Java应用程序提供。高度的灵活性加我李天生可以动态扩展的语言特性就是依赖运行期动态加载和动态链接这两个特性实现的。例如如果编写一个面向接口的应用程序可以等到运行时在指定企业实际的实现类。用户可以通过Java预定义和自定义类在加载器让一个本地的应用程序可以在运行时从网络或其他地方加载一个二进制流作为程序代码的一部分。这种组装应用程序的方式目前已经广泛应用于加我程序之中。从最基本的JSP applied要相对复杂的os gi技术。都使用了Java语言运行期内加载的特性。 为了避免语言表达中可能存在偏差在本章正式开始之前笔者先设立两个语言上的约定第一在实际情况中每个class文件都有可能代表着Java语言中的一个接口或类。课文中对类的描述都包含了类和接口的可能性而对类和接口需要分开描述的场景会特别指明第二于前面介绍class文件格。而是时约定一致。笔者本站所提到的class文件并非指某个存在于具体磁盘中的文件这里所说的class文件应当是一串二进制的自己流。无论以任何形式存在都可以。 类的加载时机 内存被加载到虚拟机内存中开始到卸载出内存为止它的整个生命周期包括加载验证准备解析初始化使用和卸载七个阶段。其中验证准备解析。三个部分统称为链接。 加载验证准备初始化和卸载这五个阶段的顺序是确定的类的加载过程必须按照这种顺序按部就班的开始而解析阶段则不一定。他在某些情况下可以在初始化之后进行开始这是为了支持Java语言的运行是绑定也称为动态绑定。或往期绑定注意这里笔者写的是按部就班的开始而不是按部就班的运行或完成强调这点是因为这些阶段通常都是相互交叉混合进行的通常会在一个阶段执行的过程中调用激活另外一个阶段。 什么情况下开始类加载过程的第一个阶段加载千瓦虚拟机规范中并没有进行强制约束这点可以。作为虚拟机的具体实现来自有把握当做于初始化阶段虚拟机规范则是严格规定了有且只有五种情况必须立即对。类进行初始化而加载验证准备自然需要在此之前开始。 1.遇到new get staticput static或Invoke static这四条字节码指令时如果累没有进行过初始化。则需要先触发及初始化发生这四条指令的最常见的Java代码团结是使用new关键字实例化对象的时候。读取或设置一个类的静态字段被final修饰已在编译器把结果放入常量池的静态字段除外。以及调用一个类的静态方法的时候。 2.使用Java.lang.reflect包的方法对类进行反射时调用的时候。如果累没有进行过初始化则需要相触发及初始化。 3.当初始化一个类的时候如果发现其父类还没有进行过的初始化。则需要先触发其父类的初始化。 4.当虚拟机启动时用户需要指定一个执行的主类包含内方法的那个类。虚拟机会先初始化这个主类 5.当时用JDK七的动态语言支持时如果一个Java Lang, dear invoke method handler.实力最后的解析结果是Refget static ref put static if evoke static的方法句柄.并且这个方法句柄所对应的类没有进行过初始化则需要先触发其初始化。 对于这五种住发类进行初始化的场景虚拟机规范中使用了一个很强烈的限定语。有且只有这五种场景中的行为称为对一个类进行。主动引用除此之外所有引用类的方法都不会触发。初始化被称为被动引用。 JVM之Java中的四种引用类型 在java中一切都是对象对象的操作是通过对象的引用实现的java中的引用对象类型有四种强、软、弱、虚。 强在java中最常见的及时前引用再把一个对象赋值给一个引用变量时这个引用变量就是一个强引用。有强引用的对象一定为可达性状态所以不会被垃圾回收机制回收因此强引用就是造成java内存溢出的主要原因。软阮引用通过softreference类实现如果一个低下只有软引用则在系统内存空间不足时该对象将被回收弱弱引用通过weakreference类实现如果一个对象只有弱引用则在垃圾回收过程中一定会被回收虚虚引用通过phantomreference类实现虚引用和引用对象联合使用主要用于追踪对象的垃圾回收状态 JVM之类加载器和双亲委派机制 JVM之类加载器 虚拟机设计团队把加载动作放到 JVM 外部实现以便让应用程序决定如何获取所需的类JVM 提供了 3 种类加载器 启动类加载器(Bootstrap ClassLoader) 负责加载 JAVA_HOME\lib 目录中的或通过-Xbootclasspath 参数指定路径中的且被虚拟机认可按文件名识别如 rt.jar的类 扩展类加载器(Extension ClassLoader) 负责加载 JAVA_HOME\lib\ext 目录中的或通过java.ext.dirs 系统变量指定路径中的类库 应用程序类加载器(Application ClassLoader) 负责加载用户路径classpath上的类库。JVM 通过双亲委派模型进行类的加载当然我们也可以通过继承java.lang.ClassLoader 实现自定义的类加载器 双亲委派机制 当一个类收到了类加载请求他首先不会尝试自己去加载这个类而是把这个请求委派给父类去完成每一个层次类加载器都是如此因此所有的加载请求都应该传送到启动类加载其中 只有当父类加载器反馈自己无法完成这个请求的时候在它的加载路径下没有找到所需加载的 Class子类加载器才会尝试自己去加载。 采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object不管是哪个加载器加载这个类最终都是委托给顶层的启动类加载器进行加载这样就保证了使用不同的类加载器最终得到的都是同样一个 Object 对象。 Java垃圾回收与算法 如何确定垃圾 java采用引用计数法和可达性分析算法来确定对象是否应该被护手其中引用激素分容易产生循环依赖的问题可达性分析算法通过根据搜索算法来实现。根据搜索算法可以一系列gc roots 的点作为起点向下搜索在一个对象到任何gc roots都没有引用链相连时说明对象已经死亡根据搜索算法主要针对栈中的引用方法区中的静态变量引用和jni中的引用展开分析。 引用计数法 在Java中如果要操作对象就必须先获得该对象的引用因此可以通过引用计数法来判断一个对象是否可以被回收。在为对象添加一个引用是引用计数1在为对象删除一个引用时减少一个引用。如果一个对象的引用计数为0 则表示该对象没有被引用可以被回收。 引用计数法容易产生循环引用的问题循环引用值两个对象互相引用导致他们的引用一直存在而不能回收 可达性分析算法 为了解决引用计数法的循环引用的问题java还采用了可达性分析算法来判断对象是否可以被回收具体做法首先定义一些gc roots 对象然后以这些对象作为起点向下搜索如果gcroots 和一个对象直接没有可达路径则称该对象是不可达的。不可达对象要经过至少两次标记才能判断是否可以被回收如果在两次标记后该对象仍然不是可达的则将被垃圾回收期回收。 哪些对象可以作为 gcroots对象呢 稍后回复 Java中常用的来及回收算法 Java中常用的垃圾回收算法有标记清除、复制、标记整理、分代回收四种垃圾回收算法 标记清除算法 标记清除算法是基础的垃圾回收算法其过程分为标记和清理两个阶段在标记阶段标记所有需要回收的对象在清除极端可回收的对象并释放所长用的内存空间。 由于标记清除算法实在清理对象锁占用的内存空间后并没有重新整理可用的内存空间因此如果内存中被护手的小对象过多则会引起内存碎片化的问题继而引起大对象无法获得连续可用的内存空间问题 复制算法 复制算法为了解决标记清除算法内存碎片化的问题而设计的复制算法首先将内存活粉两块大小相等的内存区域1区域2区域新生成的对象都被存放在1区域在1区域内的对象存储忙后会对内存1进行一次标记并将标记后仍然存活的对象全部复制到区域2中然后直接清理1区域的内存。 复制算法的内存清理效率高并且容易实现但由于同一时刻只有一个内存区域可用即可用的内存空间被压缩为原来的一半因此存在大量的内存空间浪费同时在系统中有大量长时间存活的对象是这些对象将存活在1区域和2区域之间来回复制而影响系统的运行效率。因此该算法只在对象存活时间较短时效率高。 标记整理算法 标记整理算法结合了标记清除算法和复制算法的有点其标记阶段和标记清除算法的标记阶段仙童在标记完成后存活的对象复制到内存的一端然后清除该端的对象并释放内存 分代回收算法 无论是标记清除算法、复制算法、标记整理算法都无法堆所有类型长生命手气、短生命周期、大对象、小对象的对象进行垃圾回收。因此正对不同的对象类型jvm采用了不同的垃圾回收算法该算法被称为分代回收算法 分代回收算法根据对象的不同类型将内存划分为不同的区域jvm将对划分为新生代和老年代新生代主要存放新生成的对象其特点是对象数量多但是生命周期短在每次进行垃圾回收是都有大量的堆被回收老年代的主要存放大对象和生命走起长的对象因此可回收的对象相对较少因此jvm根据不同的区域的特点选择不同的算法。 目前大部分jvm在新生代都采用了复制算法因为在新生代中每次进行垃圾回收时都有大量的 垃圾对象被回收需要复制的对象存活的比较少不存在大量的对象在内存中被来回复制的问题因此采用复制算法能安全、高效的回收新生代大量的生命周期的对象并释放内存空间。 jvm将新生代进一步划分为一块大的eden区和两块比较小的Survivor区Survivor区又分为SurvivorFrom和SurvivorTo区jvm在运行过程中主要使用eden区和SurvivorFrom区进行垃圾回收时会将eden区和SurvivorFrom区中存活的对象复制到SurvivorTo区然后清理eden区和SurvivorFrom区。 老年代主要存放声音周期比较长的对象和大对象因为每次只有少量的非存活的对象被回收因而在老年代采用标记清除算法。 在jvm中海油一个区域即方法区中的永久代永久代用存储class类、常量、方法描述等在永久代主要回收废弃的常量和无用的类。 jvm内存中的对象组要被分配到新生代中的eden区和SurvivorFrom区在少数情况下回直接分配到来年代在新生代的eden区和SurvivorFrom区的内存空间不足时会触发一次gc该过程成为minorgc。在minorgc后eden区和SurvivorFrom区中存活的对象会被复制到SurvivorTo中然后eden区和SurvivorFrom区中的对象被清理如果此时在SurvivorTo区无法找到连续的内存空间存储某个对象则将这个对象直接存储到老年代若Survivor区的对象经过一次gc之后仍然存活则其年龄1默认情况下对象在年龄达到15时将被移动到老年代。 Java中的内存区域 JVM 的内存区域分为私有区域(程序计数器、虚拟机栈、本地方法区)、线程共享区域(对、方法区)和直接内存。 线程私有区域的生命周期与线程仙童随线程的启动而创建随着线程的结束而销毁在JVM内部每个线程斗鱼操作系统的本地线程直接映射因此线程私有区域的存在与否和本地线程的启动和销毁对应 线程共享区域随着虚拟机的饿启动而创建随着关闭而销毁 直接内存也交所对外内存他并不是JVM运行时的数据区的一部分但是在并发中被频繁使用JDK的nio模块提供基于CHannel和Buffer的IO操作就是基于对堆外内存实现的nio模块通过调用本地方法库直接在操作系统上分配堆内存然后直接使用DirectByteBuffer对象作为这块内存的引用对内存进行操作Java进程可以通过对外内存技术避免在Java对和Native中来回复制数据带来的资源兰妃和性能损耗和性能消耗因此对外内存在搞并发场景下被广泛使用。 程序计数器 程序计数器 线程私有 去内存泄漏的问题它是一块很小的内存空间用于存储当前运行的线程锁执行的字节码的行号指示器每个运行中的线程中都有一个独立的程序计数器在方法正在执行时该方法的程序计数器记录的是事实虚拟机字节码指令的地址如果该方法执行的是本地方法则程序计数器的值为空 虚拟机栈 线程私有 描述Java方法的执行过程虚拟机栈是描述Java方法的执行过程的内存模型提前在当前栈针中存储了局部变量表、动态链接、方法出口等信息。同时栈针用来存储部分运行时数据及其数据结构处理动态链接方法的返回值和异常分派。 栈针用来记录方法的执行过程在方法被执行时虚拟机会为其创建一个与之对应的栈针方法的执行和返回对应栈针在虚拟机栈中的入栈和出栈无论方法时正常运行还是异常完成抛出了在方法内未被捕获的异常都是为方法运行结束。 本地方法区 本地方法区和虚拟机栈作用类似区别是虚拟机栈为执行Java方法服务本地方法栈为本地方法服务 堆 堆也叫云信使数据区线程共享。在JVM运行过程中创建的对象和产生的数据都被存储在堆中堆是被线程共享的内存区域也是垃圾回收器进行垃圾回收的最重要的内存区域。由于现代JVM采用分代回收算法因此JVM堆从GC的角度可以细分为新生代、老年代、永久代。 方法区 方法区线程共享方法区也成为永久代用于存储常量、静态变量、类信息、即时编译器编译后的机器码、运行时常量池等数据 JVM吧GC分代手机款张志方法区及时用Java堆的永久代来实现方法区这样JVM的垃圾回收期就可以像管理java堆一样管理这部分内存永久代的内存回收主要正对常量池的回收和类的卸载以你可回收的对象很少 常量被存储在运行时常量池中是方法区的一部分静态病例也属于方法区的一部分在类信息中不但保存了类的版本、字段、方法、接口等描述信息还保存了常量信息 在即时编译后代码的内容将在执行阶段类加载完成之后被保存在付费区的运行时常量池中Java虚拟机堆class文件每一部分的格式都有明确的规定只有符合jvm规范的class文件才能通过虚拟机的检查然后被装载、执行。 JVM的运行时区域 JVM的运行时区域也叫作jvm堆从gc的角度讲jvm分成新生代、老年代、永久代。其中新生代默认占1/3堆内存空间老年代默认占用2/3堆内存空间永久代占非常少的堆内存空间新生代又分为Eden区、ServivorFrom区、ServivorTo区Eden区默认占用8/10新生代空间ServivorFrom区和ServivorTo区默认占用1/10新生代空间。 新生代 新生代分为 Eden区、ServivorFrom区、ServivorTo区。JVM新创建的对象除了大对象会被存放在新生代默认占用新生代的1/3空间由于jvm会频繁创建对象所以新生代会频繁出发minorGC进行来及回收。 Eden区 java次年创建的对象首相会被存放在Eden区如果新创建的对象属于大对象则直接分配到老年区大对象的定义和具体的jvm版本堆大小和垃圾回收策略有关一般为2-128K,可通过xxPretenureSizeThreshoud设置大小在Eden区的内存空间不足时会触发minorGC对新生代进行一次垃圾回收。ServivorTo区 保留上一次minorGC时的幸存者ServivorFrom区 将上一次minorGC的幸存者作为这一次minorGC的被扫描这 新生代的gc过程叫minorGC采用复制算法实现 把Eden区和ServivorFrom区中村活的对象复制到ServivorTo区。如果某对像的年龄达到 老年代的标准对象晋升老念叨的标准由XXmaxTenuringThreshold设置默认是15将其复制到老年代同事吧这些对象的年龄1如果ServivorTo区的内存空间不够则也直接将其复制到老年代如果对象属于大对象(2-128k的对象属于大对象)则也直接将其复制到老年代清空Eden区和ServivorFrom区中的对象将ServivorTo区和ServivorFrom区互换原来ServivorTo区成为下一次GC的ServivorFrom区。 老年代 老对象主要存放长生命周期的对象和大对象老年代的gc成为majorGC在老年代对象比较稳定majorGC不会被频繁出发在进行majorGC前jvm会进行一次minorGC在minorGC后人人出现老遍地当且仅当老年代空间不足或者无法找到足够大的连续内存空间分配给新创建的大对象是会触发majorGC进行垃圾回收释放jvm的内存空间 majorGC采用标记清楚算法该算法首先会扫描所有对象并标记存活的对象然后挥手未被标记的对象释放内存空间 因为先要扫描老年代的所有对象再回收所以majorGC的耗时比较长majorGC的标记清楚算法容易产生内存碎片在老年代没还有足够存储空间可分配是会抛出oom异常 永久代 永久代值内存的永久保存区域主要存放class和meta的信息class在类加载时被放入到永久代永久代和老年代、新生代不同过程不会再程序运行过程中对永久代的内存进行清理这也导致永久代的内存会随着加载class文件的增加而增加在加载的class文件过多时会抛出oom异常比如Tomcat引用jar文件过多导致jvm内存不足而无法启动 需要注意的是在java8中永久代已经被元数据区元空间取代元数据区的作用和永久代类似二者最大的区别在于元数据并没有使用虚拟机的内存而是使用直接内存因此元空间大小不受jvm内存的限制主要和操作系统的内存有关 在java8中jvm将类的元数据放在本地内存中将常量池和类的静态变量放入到java堆中这样jvm能够加载多少元数据信息就不再有jvm的最大可用内存空间决定而是由操作系统的实际可用内存空间决定。 面试题 双亲委派模型的好处: ·主要是为了安全性避免用户自己编写的类动态替换 Java的一些核心类比如String。 同时也避免了类的重复加载因为JVM中区分不同类不仅仅是根据类名相同的class文件被不同的 ClassLoader加载就是不同的两个类 GC如何判断对象可以被回收 。引用计数法每个对象有一个引用计数属性新增一个引用时计数加1引用释放时计数减1计数为0时可以 回收 可达性分析法从 GC Roots开始向下搜索搜索所走过的路径称为引用链。当一个对象到GC Roots 没有任 何引用链相连时则证明此对象是不可用的那么虚拟机就判断是可回收对象。 引用计数法可能会出现A引用了BB又引用了A这时候就算他们都不再使用了但因为相互引用计数器1永远无法被回收。 GC Roots的对象有: 1.虚拟机栈(栈帧中的本地变量表中引用的对象 2.方法区中类静态属性引用的对象 3.方法区中常量引用的对象 4.本地方法栈中JNI(即一般说的Native方法)引用的对象 可达性算法中的不可达对象并不是立即死亡的对象拥有一次自我拯救的机会。对象被系统宣告死亡至少要经历两次标记过程第一次是经过可达性分析发现没有与GC Roots相连接的引用链第二次是在由虚拟机自动建立的EinalizerM列中判断具否雪更执行finalized方法 当对象变成(GC Roots)不可达时GC会判断该对象是否覆盖了finalize方法若未覆盖则直接将其回收。否则,若对象未执行过finalize方法将其放入F-Queue队列由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后GC会再次判断该对象是否可达若不可达则进行回收否则对象“复活1 每个对象只能触发一次finalize0方法 由于finalize0方法运行代价高昂不确定性大无法保证各个对象的调用顺序不推荐大家使用建议遗忘它。 Jdk1.7到Jdk1.8 java虚拟机发生了什么变化? 1.7中存在永久代1.8中没有永久代替换它的是元空间元空间所占的内存不是在虚拟机内部而是本地内存空间这么做的原因是不管是永久代还是元空间他们都是方法区的具体实现之所以元空间所占的内存改成本地内存官方的说法是为了和JRockit统一不过额外还有一些原因比如方法区所存储的类信息通常是比较难确定的所以对于方法区的大小是比较难指定的太小了容易出现方法区溢出太大了又会占用了太多虚拟机的内存空间而转移到本地内存后则不会影响虚拟机所占用的内存 Java的内存结构堆分为哪几部分默认年龄多大进入老年代 1.年轻代 a.Eden区8) b. From Survivor区 (1)c. To Survivor区(1)2.老年代 默认对象的年龄达到15后就会进入老年代
http://www.huolong8.cn/news/328481/

相关文章:

  • 个人网店和网站的区别广州网站开发招聘信息
  • 台州网站推广技巧付费软件服务外包人才培养专业
  • 邯郸市城市建设局网站天津软件定制开发
  • 不想用原来的网站模板了就用小偷工具采集了一个可是怎么替换网站建设多钱
  • wordpress电子商务站网站备案 互联网信息查询
  • dedecms双语网站wordpress实现选项卡
  • 彩票网站建设方案联盟营销平台
  • 电子商务网站建设的作用唐山玉田孤树做宣传上什么网站
  • 渝北网站制作专题网站搭建
  • 网站页面怎么算光谷做网站推广多少钱
  • 网站建设收费标准市场网站的数据库选择
  • 三合一网站建设系统设计师网页设计培训
  • 如何做简洁网站环球影城可以寄存东西吗
  • 合肥大型网站公司加盟代理
  • 怎么手动安装网站程序软件外包平台的服务商
  • 打开网站的语音播报怎么做苏州市工业园区规划建设局网站
  • 网页版qq空间怎么看特别关心网站优化公司seo案例
  • 做淘客需要网站wordpress图文标题一体布局
  • 个人网站学生作业网站建设心得体会范文
  • 哈尔滨高端网站设计济宁做网站优化
  • 怎么建论坛网站个人免费自助建站网站
  • 中国建设银行企业官网站网站做软件有哪些内容
  • 湛江网站公司可以做推广的网站
  • 手机网站触摸版成都网站建设网络
  • 济宁嘉祥网站建设高端企业网站建设服务商
  • 服装网站怎么做wordpress头像多说
  • ps做网站字体大小wordpress手机端菜单被挤到第二行
  • 网站制作详情关于当当网站建设方案
  • 大连网站建设 青鸟传媒自己制作头像app软件
  • 上海外贸soho网站建设wordpress 古典主题