- 浏览: 565994 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (267)
- 随笔 (4)
- Spring (13)
- Java (61)
- HTTP (3)
- Windows (1)
- CI(Continuous Integration) (3)
- Dozer (1)
- Apache (11)
- DB (7)
- Architecture (41)
- Design Patterns (11)
- Test (5)
- Agile (1)
- ORM (3)
- PMP (2)
- ESB (2)
- Maven (5)
- IDE (1)
- Camel (1)
- Webservice (3)
- MySQL (6)
- CentOS (14)
- Linux (19)
- BI (3)
- RPC (2)
- Cluster (9)
- NoSQL (7)
- Oracle (25)
- Loadbalance (7)
- Web (5)
- tomcat (1)
- freemarker (1)
- 制造 (0)
最新评论
-
panamera:
如果设置了连接需要密码,Dynamic Broker-Clus ...
ActiveMQ 集群配置 -
panamera:
请问你的最后一种模式Broker-C节点是不是应该也要修改持久 ...
ActiveMQ 集群配置 -
maosheng:
longshao_feng 写道楼主使用 文件共享 模式的ma ...
ActiveMQ 集群配置 -
longshao_feng:
楼主使用 文件共享 模式的master-slave,produ ...
ActiveMQ 集群配置 -
tanglanwen:
感触很深,必定谨记!
少走弯路的十条忠告
JVM是JAVA虚拟机(JAVA Virtual Machine)的缩写,是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JAVA虚拟机有自己完善的虚拟硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM屏蔽了与具体操作系统平台相关的信息,使得JAVA程序只需生成在JAVA虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
JVM的生命周期:
(1) JVM 实例的诞生
当启动一个JAVA程序时,一个JVM实例就产生了,任何一个拥有public static void main(String[] args)函数的class都可以作为JVM实例运行的起点。
(2) JVM 实例的运行
main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,JAVA程序也可以标明自己创建的线程是守护线程。
(3) JVM 实例的消亡
当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出。
JVM :JAVA 虚拟机
所有的JAVA程序都是运行在JVM上,JVM是JRE的一部分。
JRE : JAVA Runtime Environment(JAVA 运行环境)
JRE主要用于执行JAVA程序,JRE除了包含JVM外还包含一些基础的JAVA API,JRE是JDK的一部份。
JDK : JAVA Development Kit(JAVA 开发工具包)
JDK提供了JAVA的开发环境和运行环境(JRE),开发环境主要包含了一些开发工具,例如常用的JAVAc编译工具、jar打包执行程序、还有一些JVM监控工具等等。
JVM体系结构:
JVM 的结构基本上由四部分组成:
1.类加载器,在JVM启动时或者在类运行时将需要的Class加载到JVM中。
2.执行引擎,执行引擎的任务是负责执行Class文件中包含的字节码指令,相当于实际机器的CPU。
3.内存区,将内存划分成若干个区以模拟实际机器上的存储、记录和调度功能模块。
4.本地方法调用,调用C或C++实现的本地方法的代码返回结果。
JVM 体系结构主要包含两个子系统和两个组件:
Class Loader(类加载器)子系统,Execution Engine(执行引擎)子系统
Runtim Data Area(运行时数据区域)组件,Native Interface(本地接口)组件
Class Loader 类加载器:
类加载器负责加载 JAVA 类的字节代码到JAVA 虚拟机中,可以根据指定的类名(如java.lang.Object)来装载class文件的内容到Runtime data area中的method area(方法区域)。JAVA程序员可以extends java.lang.ClassLoader类来写自己的Class loader。
双亲委派模型工作过程
如果一个类加载器接收到了类加载的请求,它首先把这个请求委托给他的父类加载器去完成,每个层次的类加载器都是如此,因此所有的加载请求都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它在搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
优点:java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在tools.jar中,无论哪个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果用户自己写了一个名为java.lang.Object的类,并放在程序的Classpath中,那系统中将会出现多个不同的Object类,java类型体系中最基础的行为也无法保证,应用程序也会变得一片混乱。
站在JVM的角度讲,主要有两种类型加载器:启动类加载器和所有其它的类加载器。
启动类加载器是JVM实现的一部分,使用C++语言实现,其它类加载器都由java语言实现 ,独立于虚拟机外部,并且全部继承抽象类java.lang.ClassLoader
(1) Bootstrap ClassLoader 启动类加载器
这是JVM的根ClassLoader,它是用C++实现的,JVM启动时初始化此ClassLoader,并由此ClassLoader完成$JAVA_HOME$中jre\lib\rt.jar(Sun JDK的实现)中所有class文件的加载,这个jar中包含了java规范定义的所有接口以及实现。启动类加载器无法被JAVA程序直接引用。
(2) Extension ClassLoader 扩展类加载器
扩展类加载器负责加载<JAVA_HOME>\lib\ext目录中或者java.ext.dirs系统变量所指定的所有类库,开发者可以直接使用扩展类加载器。
(3)Application ClassLoader 应用程序类加载器
JVM用此classloader来加载用户类路径 (Classpath)上所指定的类库,包含指定的jar包以及目录,该加载器有时也称为系统类加载器。开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
(4) User-Defined ClassLoader 用户自定义类加载器
User-DefinedClassLoader是Java开发人员继承ClassLoader抽象类自行实现的ClassLoader,基于自定义的ClassLoader可用于加载非Classpath中的jar以及目录。
类加载器之类加载过程:
1. 加载
加载过程负责找到二进制字节码并加载至JVM中,JVM通过类名、类所在的包名通过ClassLoader来完成类的加载。
在加载阶段,虚拟机需要完成以下三件事情:
1.通过一个类的全限定名来获取定义此类的二进制字节流
2.将这个字节流所代表的静态存储结构化为方法区的运行时数据结构
3.在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口
2. 连接
链接过程负责对二进制字节码的格式进行校验、初始化装载类中的静态变量以及解析类中调用的接口、类。
(1)验证:确保被导入类的正确性
(文件格式验证、数据验证、字节码验证、符号引用验证)
(2)准备:为类变量分配内存,并将其初始化为默认值
(准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。
首先是这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。)
(3)解析:把类中的符号引用转换为直接引用
(解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中。
直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局相关的。
解析动作主要针对 类或接口、字段、类方法、接口方法四类符号引用进行。)
3. 初始化
初始化过程即为执行类中的静态初始化代码、构造器代码以及静态属性的初始化,在四种情况下初始化过程会被触发执行:调用了new;反射调用了类中的方法;子类调用了初始化;JVM启动过程中指定的初始化类。
JVM 加载类的阶段:
第一个阶段是找到.class文件并把这个文件包含的字节码加载到内存中。
第二个阶段又可以分为三个步骤,分别是字节码验证、Class类数据结构分析及相应的内存分配和最后的符号表的链接。
第三个阶段是类中静态属性和初始化赋值,以及静态块的执行等。
Execution Engine 执行引擎:
执行引擎是JVM最核心的组成部分之一,其主要是执行class中的指令,任何JVM实现的核心是Execution engine 。执行引擎可以把JAVA字节码转为机器能识别的字节码,并调用机器的指令进行计算等,不同JVM的执行效率很大程度决定于他们各自实现的Execution engine的好坏。“虚拟机”的执行引擎与“物理机”的执行引擎是比较类似的,这两种机器都有执行代码能力,其区别是物理机的执行引擎是直接建立在处理器、硬件、指令集和操作系统层面上的,而虚拟机的执行引擎是自己实现的,因此虚拟机可以自行制定指令集与执行引擎的结构体系,并且能够执行那些不被硬件直接支持的指令。
执行引擎也就是执行员一条条代码的一个流程,而代码都是包含在方法体内的,所以执行引擎本质上就是执行一个个方法所串起来的流程,对应到操作系统中一个执行流程就是一个Java线程。因为一个Java进程可以有多个同时执行的执行流程。这样说来每个Java线程就是一个执行引擎的实例,那么在一个JVM实例中就会同时有多个执行引擎在工作,这些执行引擎有的在执行用户的程序,有的在执行JVM内部的程序(如Java垃圾收集器)
每当创建一个新的线程时,JVM会为这个线程创建一个Java栈,同时会为这个线程分配一个PC寄存器,并且这个PC寄存器会指向这个线程的第一行可执行代码。每当调用一个新方法时会在这个栈上创建一个新的帧数据结构,这个栈帧会保留这个方法的一些元信息,比如这个方法中定义的局部变量、一些用来支持常量池的解析、正常方法返回及异常处理机制等。
Java执行部件:
JAVA Native Interface(JNI)本地接口:
Java本地接口(Java Native Interface,JNI)是一个标准的Java API,它支持将 Java 代码与使用其他编程语言编写的代码相集成,例如可以调用Native语言函数C\C++等。JNI是java与其它编程语言交互的接口。
Runtime Date Area 运行时数据区:
这个组件就是JVM的内存区域。
Runtime data area 主要包括五个部分:
Heap ( 堆)
Method Area( 方法区域)
VM Stack( 虚拟机栈 )
Native method stack( 本地方法栈)
Program Counter( 程序计数器)
Heap 和 Method Area是被所有线程的共享使用的;而vm stack, Program counter 和Native method stack是以线程为粒度的,每个线程独自拥有 。
1.程序计数器Program Counter Register:
程序计数器是是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。JAVA虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程”私有的内存。
2.虚拟机栈 VM stack:
与程序计数据器一样,JAVA虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是JAVA方法执行的内存模型:JAVA 栈总是和线程关联在一起的,每当创建一个线程时,JVM就会为这个线程创建一个对应的Java栈,在这个Java栈中又会包含多个栈帧(Stack Frame),每个方法被执行时都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入到出栈的过程 。
每当一个方法执行完成时,这个栈帧就会弹出栈帧的元素作为这个方法的返回值,并清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法,PC寄存器也会指向这个地址。只有这个活动的栈帧的本地变量可以被操作栈使用,当在这个栈帧中调用另外一个方法时,与之对应的一个新的栈帧又被创建,这个新创建的栈帧又被放到Java栈的顶部,变为当前的活动栈帧。同样现在只有这个栈帧的本地变量可能被使用,当在这个栈帧中所有指令执行完成时这个栈帧移出Java栈,刚才的那个栈帧又变为活动栈帧,前面的栈帧的返回值又变为这个栈帧的操作栈中的一个操作数。如果前面的栈帧没有返回值,那么当前的栈帧的操作栈的操作数没有变化。
VM Stack:java的线程栈,java分配一个对象时,对象的具体内容在堆中,而对象的引用则位于栈中,也就是这里。而且方法的局部变量以及函数地址的调入和调出,都存放在这里,所以,JAVA的一个对象,有两部分,对象本身的值在堆中,而引用则在栈中。当栈空间不够时,比如递归层次太深,就会导致java.lang.StackOverflowError异常。
局部变量表存放了编译期可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double)、对象引用(reference类型)和returnAddress类型(指向了一条字节码指令的地址)。
在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。
3.本地方法栈Native Method stack:
本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行JAVA方法服务的,而本地方法栈则是为虚拟机使用到的本地方法服务。本地方法栈是为JVM运行Native方法准备的空间,虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。例如Sun HotSpot虚拟机直接把本地方法栈和虚拟机栈合二为一。
在JVM规范中没有对这个区域的严格限制,他可以由不同的JVM实现者3实现,但是他和其他存储区一样也会抛出OutOfMemoryError和StackOverflowError。
4.方法区 Method area:
方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息等。当开发人员在程序中通过Class对象中的getName等方法来获取信息时,这些数据都来源于方法区域。
方法区域也是全局共享的,因此会涉及到多线种访问的同步问题,方法区在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出JAVA.lang.OutOfMemoryError:PermGen full的错误信息。
在Sun JVM中这块区域对应的为Permanet Generation,又称为持久代, Permanet Generation实际上并不等同于方法区,只不过是Hotspot JVM用Permanet Generation来实现方法区而已,有些虚拟机也没有PermSpace而是用其他机制来实现方法区。
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
既然运行时常量池是方法区的一部分,自然会受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。
5.Heap 堆空间:
对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
Java堆是垃圾收集器管理的主要区域。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。
堆空间是JAVA对象生死存亡的地区,JAVA对象的出生,成长,死亡都在这个区域完成。JAVA程序在运行时创建的所有类实例或数组都放在堆中。
每一个JAVA程序独占一个JVM实例, 一个JVM实例只存在一个堆空间,因些每个JAVA程序都有它自己的堆空间,它们不会彼此干扰。
同一个JAVA程序的多个线程都共享着同一个堆空间,所以就需要考虑多线程访问对象(堆数据)的同步问题。
Sun的 HotSpot虚拟机对于堆内存共划分为二大部分:
年轻代 / 新生代( Young Generation )、老年代( Old Generation)
大部分新生成的对象都放在Eden中,当Eden内存不够用时,触发Young GC,这时,会将Eden中不能被释放的对象以及S0中幸存的对象,都Copy到S1中,并且将经历过几次Young GC还幸存的新生代对象,放入到Old Generation中,然后释放Eden和S0。
所以S0和S1是来回切换使用的,保存新生代中还不能被释放的对象,所以S0和S1总有一个会是空的,当然在发生Youg GC时,对象正在COPY时会是二者都有数据;如果经历几次Young GC时新生代还是满的,还不能够接收Eden中过来的幸存对象,就会抛出java.lang.OutOfMemorryError:java heap space;
如果在进行Youg GC时,发现S0不够用时,则直接将对象放入Old Generation中,这时如果Old Generation内存也不够时,则触发Full GC,Full GC后如果Old Generation还是满的,就抛出内存溢出异常:java.lang.OutOfMemorryError:java heap space;
所以,假设有一个永不销毁的对象,其经历的过程如下:首先在创建时放入Eden,当某个时刻Eden满了时,通过Young GC放入S0或者S1,其在S0或者S1经历过几次Youg GC后,放入到Old generation中,当Old Generation满了的时候,发生Full GC.
1. Young (年轻代)
jvm规范中的 Heap的一部份, 年轻代又分三个区:一个Eden区,两个Survivor区
(1) 伊甸园(Eden space ):
JAVA堆空间中的大部分对象在此出生,该区的名字因此而得名。也即是说当你的JAVA程序运行时,需要创建新的对象时,JVM都将在该区为你创建一个指定的对象供程序使用。
(2) 幸存者0 区(Survivor 0 space )和幸存者1 区(Survivor1 space ):
当伊甸园的空间用完时,程序又需要创建对象;此时JVM的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的还存活的对象 移动到幸存者0区或1区。幸存者两个区就是用于存放伊甸园垃圾回收时所幸存下来的JAVA对象。
Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。
2. Tenured (老年代)
在年轻代中经历了多次垃圾回收后仍然存活的对象,就会被放到老年代中。因此,可以认为老年代中存放的都是一些生命周期较长的对象。另外一些大对象也会直接进入老年代,可以通过设置jvm参数来指定多大对象直 接进入老年代( 参数为-XX:PretenureSizeThreshold=1024,单位为字节)。
程序计数器、虚拟机栈、本地方法栈三个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作,这几个区域内不需要过多考虑回收的问题,因为方法结束或线程结束时,内存自然就跟随着回收了。而Java堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾回收器所关注的是这部分内存。
JVM内存分配主要基于两种:堆和栈
每个Java应用都唯一对应一个JVM实例,每个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用程序所有的线程共享。在Java中分配堆内存时自动初始化的,所有对象的存储空间都是在堆中分配的,但是这个对象的引用确实在栈中分配的。也就是说在建立一个对象时两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在栈中分配的内存只是一个指向这个堆对象。
-XX:PermSize=10M -XX:MaxPermSize=10M 限制方法区的大小
-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError 限制Java堆的大小,并出现异常时Dump出当前的内存堆转储快照
-Xss128K 限制栈内存容量
运行时栈帧结构:
1.局部变量表:
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。
2.操作数栈
操作数栈也常被称为操作栈,他是一个后入先出(Last In First Out,LIFO)栈。
3.动态连接
每个栈帧都包含一个执行运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。
4.方法返回地址
当一个方法被执行后,有两种方式退出这个方法。第一种方法是执行引擎遇到任意一个方法返回的字节码指令,这时候可能有返回值传递给上层的方法调用者,是否有返回值和返回值的类型将根据遇到何种方法返回指令决定,这种退出方法的方式成为正常完成出口。
另一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用throw字节码指令产生的异常,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式成为异常完成出口。一个方法使用异常完成出口的方式退出,是不会给他的上层调用者产生任何返回值的。
无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复他的上层方法的执行桩体。一般来说,方法正常退出时,调用者的PC计数器的值就可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这行信息。
方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:回复上层方法的局部变量表和操作数栈,把返回值(如果有)压入调用者栈帧的操作数中,调整PC计数器的值以指向方法调用指令后面的一条指令等。
5.附加信息
JVM参数的含义:
-Xms :初始堆大小,默认值为物理内存的1/64(<1GB),默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制。
-Xmx :最大堆大小,默认值为物理内存的1/4(<1GB),默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。
-Xmn : 年轻代大小(1.4or lator),此处的大小是(eden + 2 survivor space),整个堆大小=年轻代大小 + 年老代大小 + 持久代大小,增大年轻代后,将会减小年老代大小,此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-XX:PermSize :设置持久代(perm gen)初始值,默认值为物理内存的1/64
-XX:MaxPermSize :设置持久代最大值,默认值为物理内存的1/4
-Xss :每个线程的堆栈大小,JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右,一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。
-XX:NewRatio :年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代),-XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5,Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatio :Eden区与Survivor区的大小比值,默认值设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。
-XX:LargePageSizeInBytes :内存页的大小不可设置过大, 会影响Perm的大小,默认值为128m。
JVM的生命周期:
(1) JVM 实例的诞生
当启动一个JAVA程序时,一个JVM实例就产生了,任何一个拥有public static void main(String[] args)函数的class都可以作为JVM实例运行的起点。
(2) JVM 实例的运行
main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,JAVA程序也可以标明自己创建的线程是守护线程。
(3) JVM 实例的消亡
当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出。
JVM :JAVA 虚拟机
所有的JAVA程序都是运行在JVM上,JVM是JRE的一部分。
JRE : JAVA Runtime Environment(JAVA 运行环境)
JRE主要用于执行JAVA程序,JRE除了包含JVM外还包含一些基础的JAVA API,JRE是JDK的一部份。
JDK : JAVA Development Kit(JAVA 开发工具包)
JDK提供了JAVA的开发环境和运行环境(JRE),开发环境主要包含了一些开发工具,例如常用的JAVAc编译工具、jar打包执行程序、还有一些JVM监控工具等等。
JVM体系结构:
JVM 的结构基本上由四部分组成:
1.类加载器,在JVM启动时或者在类运行时将需要的Class加载到JVM中。
2.执行引擎,执行引擎的任务是负责执行Class文件中包含的字节码指令,相当于实际机器的CPU。
3.内存区,将内存划分成若干个区以模拟实际机器上的存储、记录和调度功能模块。
4.本地方法调用,调用C或C++实现的本地方法的代码返回结果。
JVM 体系结构主要包含两个子系统和两个组件:
Class Loader(类加载器)子系统,Execution Engine(执行引擎)子系统
Runtim Data Area(运行时数据区域)组件,Native Interface(本地接口)组件
Class Loader 类加载器:
类加载器负责加载 JAVA 类的字节代码到JAVA 虚拟机中,可以根据指定的类名(如java.lang.Object)来装载class文件的内容到Runtime data area中的method area(方法区域)。JAVA程序员可以extends java.lang.ClassLoader类来写自己的Class loader。
双亲委派模型工作过程
如果一个类加载器接收到了类加载的请求,它首先把这个请求委托给他的父类加载器去完成,每个层次的类加载器都是如此,因此所有的加载请求都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它在搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
优点:java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在tools.jar中,无论哪个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果用户自己写了一个名为java.lang.Object的类,并放在程序的Classpath中,那系统中将会出现多个不同的Object类,java类型体系中最基础的行为也无法保证,应用程序也会变得一片混乱。
站在JVM的角度讲,主要有两种类型加载器:启动类加载器和所有其它的类加载器。
启动类加载器是JVM实现的一部分,使用C++语言实现,其它类加载器都由java语言实现 ,独立于虚拟机外部,并且全部继承抽象类java.lang.ClassLoader
(1) Bootstrap ClassLoader 启动类加载器
这是JVM的根ClassLoader,它是用C++实现的,JVM启动时初始化此ClassLoader,并由此ClassLoader完成$JAVA_HOME$中jre\lib\rt.jar(Sun JDK的实现)中所有class文件的加载,这个jar中包含了java规范定义的所有接口以及实现。启动类加载器无法被JAVA程序直接引用。
(2) Extension ClassLoader 扩展类加载器
扩展类加载器负责加载<JAVA_HOME>\lib\ext目录中或者java.ext.dirs系统变量所指定的所有类库,开发者可以直接使用扩展类加载器。
(3)Application ClassLoader 应用程序类加载器
JVM用此classloader来加载用户类路径 (Classpath)上所指定的类库,包含指定的jar包以及目录,该加载器有时也称为系统类加载器。开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
(4) User-Defined ClassLoader 用户自定义类加载器
User-DefinedClassLoader是Java开发人员继承ClassLoader抽象类自行实现的ClassLoader,基于自定义的ClassLoader可用于加载非Classpath中的jar以及目录。
类加载器之类加载过程:
1. 加载
加载过程负责找到二进制字节码并加载至JVM中,JVM通过类名、类所在的包名通过ClassLoader来完成类的加载。
在加载阶段,虚拟机需要完成以下三件事情:
1.通过一个类的全限定名来获取定义此类的二进制字节流
2.将这个字节流所代表的静态存储结构化为方法区的运行时数据结构
3.在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口
2. 连接
链接过程负责对二进制字节码的格式进行校验、初始化装载类中的静态变量以及解析类中调用的接口、类。
(1)验证:确保被导入类的正确性
(文件格式验证、数据验证、字节码验证、符号引用验证)
(2)准备:为类变量分配内存,并将其初始化为默认值
(准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。
首先是这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。)
(3)解析:把类中的符号引用转换为直接引用
(解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中。
直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局相关的。
解析动作主要针对 类或接口、字段、类方法、接口方法四类符号引用进行。)
3. 初始化
初始化过程即为执行类中的静态初始化代码、构造器代码以及静态属性的初始化,在四种情况下初始化过程会被触发执行:调用了new;反射调用了类中的方法;子类调用了初始化;JVM启动过程中指定的初始化类。
JVM 加载类的阶段:
第一个阶段是找到.class文件并把这个文件包含的字节码加载到内存中。
第二个阶段又可以分为三个步骤,分别是字节码验证、Class类数据结构分析及相应的内存分配和最后的符号表的链接。
第三个阶段是类中静态属性和初始化赋值,以及静态块的执行等。
Execution Engine 执行引擎:
执行引擎是JVM最核心的组成部分之一,其主要是执行class中的指令,任何JVM实现的核心是Execution engine 。执行引擎可以把JAVA字节码转为机器能识别的字节码,并调用机器的指令进行计算等,不同JVM的执行效率很大程度决定于他们各自实现的Execution engine的好坏。“虚拟机”的执行引擎与“物理机”的执行引擎是比较类似的,这两种机器都有执行代码能力,其区别是物理机的执行引擎是直接建立在处理器、硬件、指令集和操作系统层面上的,而虚拟机的执行引擎是自己实现的,因此虚拟机可以自行制定指令集与执行引擎的结构体系,并且能够执行那些不被硬件直接支持的指令。
执行引擎也就是执行员一条条代码的一个流程,而代码都是包含在方法体内的,所以执行引擎本质上就是执行一个个方法所串起来的流程,对应到操作系统中一个执行流程就是一个Java线程。因为一个Java进程可以有多个同时执行的执行流程。这样说来每个Java线程就是一个执行引擎的实例,那么在一个JVM实例中就会同时有多个执行引擎在工作,这些执行引擎有的在执行用户的程序,有的在执行JVM内部的程序(如Java垃圾收集器)
每当创建一个新的线程时,JVM会为这个线程创建一个Java栈,同时会为这个线程分配一个PC寄存器,并且这个PC寄存器会指向这个线程的第一行可执行代码。每当调用一个新方法时会在这个栈上创建一个新的帧数据结构,这个栈帧会保留这个方法的一些元信息,比如这个方法中定义的局部变量、一些用来支持常量池的解析、正常方法返回及异常处理机制等。
Java执行部件:
JAVA Native Interface(JNI)本地接口:
Java本地接口(Java Native Interface,JNI)是一个标准的Java API,它支持将 Java 代码与使用其他编程语言编写的代码相集成,例如可以调用Native语言函数C\C++等。JNI是java与其它编程语言交互的接口。
Runtime Date Area 运行时数据区:
这个组件就是JVM的内存区域。
Runtime data area 主要包括五个部分:
Heap ( 堆)
Method Area( 方法区域)
VM Stack( 虚拟机栈 )
Native method stack( 本地方法栈)
Program Counter( 程序计数器)
Heap 和 Method Area是被所有线程的共享使用的;而vm stack, Program counter 和Native method stack是以线程为粒度的,每个线程独自拥有 。
1.程序计数器Program Counter Register:
程序计数器是是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。JAVA虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程”私有的内存。
2.虚拟机栈 VM stack:
与程序计数据器一样,JAVA虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是JAVA方法执行的内存模型:JAVA 栈总是和线程关联在一起的,每当创建一个线程时,JVM就会为这个线程创建一个对应的Java栈,在这个Java栈中又会包含多个栈帧(Stack Frame),每个方法被执行时都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入到出栈的过程 。
每当一个方法执行完成时,这个栈帧就会弹出栈帧的元素作为这个方法的返回值,并清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法,PC寄存器也会指向这个地址。只有这个活动的栈帧的本地变量可以被操作栈使用,当在这个栈帧中调用另外一个方法时,与之对应的一个新的栈帧又被创建,这个新创建的栈帧又被放到Java栈的顶部,变为当前的活动栈帧。同样现在只有这个栈帧的本地变量可能被使用,当在这个栈帧中所有指令执行完成时这个栈帧移出Java栈,刚才的那个栈帧又变为活动栈帧,前面的栈帧的返回值又变为这个栈帧的操作栈中的一个操作数。如果前面的栈帧没有返回值,那么当前的栈帧的操作栈的操作数没有变化。
VM Stack:java的线程栈,java分配一个对象时,对象的具体内容在堆中,而对象的引用则位于栈中,也就是这里。而且方法的局部变量以及函数地址的调入和调出,都存放在这里,所以,JAVA的一个对象,有两部分,对象本身的值在堆中,而引用则在栈中。当栈空间不够时,比如递归层次太深,就会导致java.lang.StackOverflowError异常。
局部变量表存放了编译期可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double)、对象引用(reference类型)和returnAddress类型(指向了一条字节码指令的地址)。
在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。
3.本地方法栈Native Method stack:
本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行JAVA方法服务的,而本地方法栈则是为虚拟机使用到的本地方法服务。本地方法栈是为JVM运行Native方法准备的空间,虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。例如Sun HotSpot虚拟机直接把本地方法栈和虚拟机栈合二为一。
在JVM规范中没有对这个区域的严格限制,他可以由不同的JVM实现者3实现,但是他和其他存储区一样也会抛出OutOfMemoryError和StackOverflowError。
4.方法区 Method area:
方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息等。当开发人员在程序中通过Class对象中的getName等方法来获取信息时,这些数据都来源于方法区域。
方法区域也是全局共享的,因此会涉及到多线种访问的同步问题,方法区在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出JAVA.lang.OutOfMemoryError:PermGen full的错误信息。
在Sun JVM中这块区域对应的为Permanet Generation,又称为持久代, Permanet Generation实际上并不等同于方法区,只不过是Hotspot JVM用Permanet Generation来实现方法区而已,有些虚拟机也没有PermSpace而是用其他机制来实现方法区。
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
既然运行时常量池是方法区的一部分,自然会受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。
5.Heap 堆空间:
对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
Java堆是垃圾收集器管理的主要区域。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。
堆空间是JAVA对象生死存亡的地区,JAVA对象的出生,成长,死亡都在这个区域完成。JAVA程序在运行时创建的所有类实例或数组都放在堆中。
每一个JAVA程序独占一个JVM实例, 一个JVM实例只存在一个堆空间,因些每个JAVA程序都有它自己的堆空间,它们不会彼此干扰。
同一个JAVA程序的多个线程都共享着同一个堆空间,所以就需要考虑多线程访问对象(堆数据)的同步问题。
Sun的 HotSpot虚拟机对于堆内存共划分为二大部分:
年轻代 / 新生代( Young Generation )、老年代( Old Generation)
大部分新生成的对象都放在Eden中,当Eden内存不够用时,触发Young GC,这时,会将Eden中不能被释放的对象以及S0中幸存的对象,都Copy到S1中,并且将经历过几次Young GC还幸存的新生代对象,放入到Old Generation中,然后释放Eden和S0。
所以S0和S1是来回切换使用的,保存新生代中还不能被释放的对象,所以S0和S1总有一个会是空的,当然在发生Youg GC时,对象正在COPY时会是二者都有数据;如果经历几次Young GC时新生代还是满的,还不能够接收Eden中过来的幸存对象,就会抛出java.lang.OutOfMemorryError:java heap space;
如果在进行Youg GC时,发现S0不够用时,则直接将对象放入Old Generation中,这时如果Old Generation内存也不够时,则触发Full GC,Full GC后如果Old Generation还是满的,就抛出内存溢出异常:java.lang.OutOfMemorryError:java heap space;
所以,假设有一个永不销毁的对象,其经历的过程如下:首先在创建时放入Eden,当某个时刻Eden满了时,通过Young GC放入S0或者S1,其在S0或者S1经历过几次Youg GC后,放入到Old generation中,当Old Generation满了的时候,发生Full GC.
1. Young (年轻代)
jvm规范中的 Heap的一部份, 年轻代又分三个区:一个Eden区,两个Survivor区
(1) 伊甸园(Eden space ):
JAVA堆空间中的大部分对象在此出生,该区的名字因此而得名。也即是说当你的JAVA程序运行时,需要创建新的对象时,JVM都将在该区为你创建一个指定的对象供程序使用。
(2) 幸存者0 区(Survivor 0 space )和幸存者1 区(Survivor1 space ):
当伊甸园的空间用完时,程序又需要创建对象;此时JVM的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的还存活的对象 移动到幸存者0区或1区。幸存者两个区就是用于存放伊甸园垃圾回收时所幸存下来的JAVA对象。
Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。
2. Tenured (老年代)
在年轻代中经历了多次垃圾回收后仍然存活的对象,就会被放到老年代中。因此,可以认为老年代中存放的都是一些生命周期较长的对象。另外一些大对象也会直接进入老年代,可以通过设置jvm参数来指定多大对象直 接进入老年代( 参数为-XX:PretenureSizeThreshold=1024,单位为字节)。
程序计数器、虚拟机栈、本地方法栈三个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作,这几个区域内不需要过多考虑回收的问题,因为方法结束或线程结束时,内存自然就跟随着回收了。而Java堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾回收器所关注的是这部分内存。
JVM内存分配主要基于两种:堆和栈
每个Java应用都唯一对应一个JVM实例,每个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用程序所有的线程共享。在Java中分配堆内存时自动初始化的,所有对象的存储空间都是在堆中分配的,但是这个对象的引用确实在栈中分配的。也就是说在建立一个对象时两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在栈中分配的内存只是一个指向这个堆对象。
-XX:PermSize=10M -XX:MaxPermSize=10M 限制方法区的大小
-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError 限制Java堆的大小,并出现异常时Dump出当前的内存堆转储快照
-Xss128K 限制栈内存容量
运行时栈帧结构:
1.局部变量表:
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。
2.操作数栈
操作数栈也常被称为操作栈,他是一个后入先出(Last In First Out,LIFO)栈。
3.动态连接
每个栈帧都包含一个执行运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。
4.方法返回地址
当一个方法被执行后,有两种方式退出这个方法。第一种方法是执行引擎遇到任意一个方法返回的字节码指令,这时候可能有返回值传递给上层的方法调用者,是否有返回值和返回值的类型将根据遇到何种方法返回指令决定,这种退出方法的方式成为正常完成出口。
另一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用throw字节码指令产生的异常,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式成为异常完成出口。一个方法使用异常完成出口的方式退出,是不会给他的上层调用者产生任何返回值的。
无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复他的上层方法的执行桩体。一般来说,方法正常退出时,调用者的PC计数器的值就可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这行信息。
方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:回复上层方法的局部变量表和操作数栈,把返回值(如果有)压入调用者栈帧的操作数中,调整PC计数器的值以指向方法调用指令后面的一条指令等。
5.附加信息
JVM参数的含义:
-Xms :初始堆大小,默认值为物理内存的1/64(<1GB),默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制。
-Xmx :最大堆大小,默认值为物理内存的1/4(<1GB),默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。
-Xmn : 年轻代大小(1.4or lator),此处的大小是(eden + 2 survivor space),整个堆大小=年轻代大小 + 年老代大小 + 持久代大小,增大年轻代后,将会减小年老代大小,此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-XX:PermSize :设置持久代(perm gen)初始值,默认值为物理内存的1/64
-XX:MaxPermSize :设置持久代最大值,默认值为物理内存的1/4
-Xss :每个线程的堆栈大小,JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右,一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。
-XX:NewRatio :年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代),-XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5,Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatio :Eden区与Survivor区的大小比值,默认值设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。
-XX:LargePageSizeInBytes :内存页的大小不可设置过大, 会影响Perm的大小,默认值为128m。
发表评论
-
java 类的加载 以及 ClassLoader
2020-04-16 09:43 450Class Loader 类加载器: 类加载器负责加载 ... -
Stack 的实现原理深入剖析
2020-04-06 13:26 491Stack 介绍: Stack是栈。 ... -
Vector 的实现原理深入剖析
2020-04-06 13:17 366Vector介绍: Vector 是矢量队列,它是JDK1. ... -
JDK 分析工具
2020-04-05 17:30 367常用分析工具: jps:显示指定系统中所有的HotSpot虚 ... -
二叉树的深度优先遍历和广度优先遍历
2020-03-10 09:33 597概述: 1、深度优先遍历(Depth-First-Sear ... -
Hashtable 的实现原理深入剖析
2020-02-18 20:59 535一、Hashtable的基本方法: 1、定义: HashT ... -
jdk 1.8 新特性
2020-02-17 13:43 3651、default关键字 ... -
Java IO 架构
2019-11-11 16:39 351主要两类: 磁盘I/O 网络I/O 基于字节 ... -
Java 数据结构与算法
2019-04-03 10:25 518程序=数据结构+算法 ... -
Java语言异常(Exception)
2018-10-09 11:40 549异常,是Java中非常常用 ... -
Java并发问题--乐观锁与悲观锁以及乐观锁的一种实现方式-CAS
2018-08-17 09:47 1473首先介绍一些乐观锁与 ... -
Java 高性能编程注意事项
2016-11-17 09:55 6471. 尽量在合适的场合使用单例 使用单例可以减轻加载的负担, ... -
Netty 解析
2017-03-07 13:47 1221Linux网络IO模型: Linux ... -
2016年Java 面试题总结
2016-01-18 13:34 54793多线程、并发及线程的基础问题: 1)Java 中能创建 vo ... -
java 内存模型
2015-12-29 13:44 816JAVA内存模型: Java内存 ... -
Java 并发编程_Synchronized
2015-12-16 12:42 872硬件的效率和一致性: 由于计算机的运算速度和它的存储和通讯子 ... -
Java 并发编程_Volatile
2015-12-15 13:42 620术语定义: 共享变量:在多个线程之间能够被共享的变量被称为共 ... -
Java 并发编程_ConcurrentLinkedQueue
2015-12-15 13:32 908ConcurrentLinkedQueue 的分析和使用: ... -
Java 并发编程_ConcurrentHashMap
2015-11-10 11:30 831ConcurrentHashMap 的分析和 ... -
JVM 垃圾回收原理
2015-10-29 13:38 489基本回收算法: 1.引用 ...
相关推荐
根据提供的文件信息,本文将对《深入剖析 Tomcat》这一资料进行详细的知识点解析。Tomcat作为一款开源的Servlet容器,被广泛应用于Java Web应用程序的部署与运行环境中。本资料旨在帮助读者深入了解Tomcat的工作原理...
本文将深入剖析JVM内存模型的结构和工作机理,并讨论如何优化JVM参数以提高Java应用程序的性能。 一、JVM内存模型结构 JVM内存模型主要分为以下几个部分: 1. 程序计数器(PC Register):存储当前线程执行的字节...
《深入剖析Tomcat》是一本专注于Java Web服务器Tomcat的深度解析资料,包含了对Tomcat源码的细致分析。此资料包提供了多个文件,包括"深入剖析Tomcat源码.rar","深入剖析tomcat.pdf",以及"apache-tomcat-7.0.32-...
《深入理解JVM & G1 GC》一书深入剖析了Java虚拟机(JVM)的工作原理,特别是针对垃圾收集器(GC)中的G1(Garbage-First)算法进行了详尽的探讨。JVM是Java程序运行的基础,它负责解析、编译、执行Java代码,并管理...
《深入剖析Tomcat》这本书是理解Apache Tomcat服务器工作原理的重要参考资料,非扫描版的特点意味着内容清晰、可读性强,方便读者复制和引用。Tomcat作为一款广泛应用的开源Java Servlet容器,它的性能优化、配置...
通过以上分析可以看出,《玩转电商系统深入剖析智慧电商平台》这本书不仅涵盖了电商平台的核心技术要点,还针对实际应用场景提出了具体的实践方案。无论是对于想要深入了解该领域的技术人员还是正在寻求解决方案的...
【JVM的深入剖析】 Java虚拟机(JVM)是Java技术的核心组成部分,它使得Java程序能够在各种操作系统和硬件平台上无缝运行,实现了平台无关性。Java由编程语言、类文件格式、虚拟机和应用程序接口(API)四个部分...
2. **内存管理**:这部分内容会深入剖析JVM的内存模型,包括堆、栈、方法区、本地方法栈等,以及它们各自的作用。特别是对Java内存模型(JMM)的讲解,有助于理解线程间的共享变量访问规则。 3. **类加载机制**:书...
由于提供的文件内容部分信息不完整,且存在重复链接和OCR扫描技术导致的错误,以下内容将专注于对Tomcat服务器的深入剖析,并不会涉及链接信息,且尽力保证知识点的准确性和完整性。 Tomcat是Apache Jakarta项目中...
《JAVA核心知识点整理》这份资料全面涵盖了Java开发者在面试中可能会遇到的各种问题,从JVM深入剖析到Java集合、多线程开发,再到Spring框架、微服务架构、网络通信、日志管理,以及分布式系统中的关键组件如...
虽然描述部分的信息重复,但从标题可以看出,文章的主要目的是围绕Java虚拟机(JVM)进行深入剖析。接下来,我们将详细阐述JVM的基本概念、架构组成以及其内部运作机制等关键知识点。 ### Java虚拟机(JVM)基本概念 ...
【深入剖析Tomcat】 Tomcat是一款广泛使用的开源Java Servlet容器,由Apache软件基金会开发和维护。它是基于Java的Web应用程序的服务器,特别是用于部署和运行Java Servlets和JSP(JavaServer Pages)技术。本篇...
### 深入剖析Tomcat(中文版)关键知识点 #### 一、Tomcat简介 Apache Tomcat是一款开源的轻量级Servlet容器,用于运行Java Servlet和JavaServer Pages(JSP)。它由Apache软件基金会的Jakarta项目开发,并且是...
Java JVM剖析是Java开发者深化技能的关键领域,它涵盖了Java虚拟机的工作原理,内存管理,以及如何优化代码性能。深入理解JVM有助于我们更好地掌控程序运行时的状态,避免内存泄漏,提高程序效率,甚至解决复杂的...
《深入剖析TOMCAT+Tomcat权威指南(第二版)》是两本关于Apache Tomcat服务器的重量级著作,它们详尽地阐述了Tomcat的内部工作机制、配置、优化以及故障排查等方面的知识,旨在帮助读者从新手到专家,全面掌握这款广泛...
《深入理解JVM虚拟机》是一本深受Java开发者喜爱的经典著作,它详尽地剖析了Java虚拟机的工作原理,帮助读者提升对Java程序运行机制的深入理解。这本书的第二版更是加入了更多最新的JVM特性,使得学习内容更加全面且...
JIT编译器是Java性能优化的关键,它通过将字节码转换为本地机器码并进行优化,显著提高了Java程序的执行效率。通过理解JIT编译器的工作原理和优化技术,开发者可以更好地编写高效的Java代码。JIT编译器的存在使得...
Java虚拟机(JVM)是Java程序运行的核心组件,它为开发者提供了跨平台的执行环境。深入理解JVM内核的原理、诊断与...通过学习,你将能够运用各种工具和技术,对复杂的Java应用进行深入剖析,让JVM成为你的得力助手。
阿里巴巴的扫地僧多隆,以其深厚的Java技术底蕴,为我们带来了关于JVM和NIO的深度剖析,旨在帮助开发者对这两个关键领域有更深入的理解。 **JVM解析** 1. **JVM架构**:JVM由类加载器、运行时数据区、执行引擎、...
本部分我们将深入探讨JVM中的类加载器,特别是根类加载器、扩展类加载器和系统类加载器。 首先,让我们了解类加载的基本过程。当JVM启动时,会触发类加载。这个过程分为三个阶段:加载、链接和初始化。加载阶段,类...