精华内容
下载资源
问答
  • Java虚拟机内存结构及编码实战

    万次阅读 多人点赞 2019-12-23 08:48:10
    深度解析了JVM的内存结构,图文并茂,生动形象。并编写多个示例代码,展示了JDK中各个不同的内存结构,详细讲解了不同版本JDK对内存结构实现的区别。

    了解JVM内存结构的目的

    在Java的开发过程中,因为有JVM自动内存管理机制,不再需要像在C、C++开发那样手动释放对象的内存空间,不容易出现内存泄漏和内存溢出的问题。但是,正是由于把内存管理的权利交给了JVM,一旦出现内存泄漏和内存溢出方面的问题,如果不了解JVM是如何使用内存的,不了解JVM的内存结构是什么样子的,就很难找到问题的根源,就更难以解决问题。

    JVM内存结构简介

    在JVM所管理的内存中,大致分为以下几个运行时数据区域:

    • 程序计数器:当前线程所执行的字节码的行号指示器。
    • 虚拟机栈:Java方法执行的内存模型,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
    • 本地方法栈:本地方法执行的内存模型,和虚拟机栈非常相似,其区别是本地方法栈为JVM使用到的Native方法服务。
    • 堆:用于存储对象实例,是垃圾收集器管理的主要区域。
    • 方法区:用于存储已被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

    其中,黄色区域的程序计数器、虚拟机栈和本地方法栈是线程私有的,红色区域的堆和方法区是线程共享的。下面我们逐一详细分析各个区域。

    程序计数器

    程序计数器(Program Counter Register)是一块较小的内存空间,它记录了当前线程所执行的字节码的行号。在JVM的概念模型里,字节码解释器工作时就是通过改变它的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都是依赖它来完成的。

    通过线程轮流切换并分配处理器执行时间,实现了JVM的多线程操作。在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,称这类内存区域为“线程私有”的内存。

    如果线程正在执行的是一个Java方法,那么它记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Natvie方法,它的值就为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

    虚拟机栈

    与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,如上图每一个线程都有自己的虚拟机栈,它的生命周期与线程相同,当线程被创建时,虚拟机栈也同时被创建;当线程被销毁时,虚拟机栈也同时被销毁。

    在线程内部,每个方法被执行的时候都会同时创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息,如上图。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

    其中栈帧中的局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型)和returnAddress类型(指向了一条字节码指令的地址)。

    其中64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其余的数据类型只占用1个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

    在Java虚拟机规范中,对这个区域规定了两种异常状况:

    如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常,让我们写一段代码,使其抛出该异常:

    /**
     * VM Args: -Xss128k
     */
    public class JVMStackSOF {
    	private int stackLength = 1;
    
    	public void stackLeak() {
    		stackLength++;
    		stackLeak();
    	}
    
    	public static void main(String[] args) {
    		JVMStackSOF sof = new JVMStackSOF();
    		try {
    			sof.stackLeak();
    		} catch (Throwable e) {
    			System.out.println("Stack length:" + sof.stackLength);
    			throw e;
    		}
    	}
    }
    

    在运行之前,设置JVM的参数为-Xss128k,运行结果如下:

    Stack length:1002
    Exception in thread "main" java.lang.StackOverflowError
    	at OneMoreStudy.JVMStackSOF.stackLeak(JVMStackSOF.java:10)
    	at OneMoreStudy.JVMStackSOF.stackLeak(JVMStackSOF.java:11)
    	at OneMoreStudy.JVMStackSOF.stackLeak(JVMStackSOF.java:11)
    ......
    

    栈的深度达到1002时,抛出了StackOverflowError异常。

    如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常,还是让我们写一段代码,使其抛出该异常:

    /**
     * VM Args: -Xss2M
     */
    public class JVMStackOOM {
    	private void dontStop() {
    		while (true) {
    
    		}
    	}
    
    	public void stackLeakByThread() {
    		while (true) {
    			Thread t = new Thread(new Runnable() {
    				public void run() {
    					dontStop();
    				}
    			});
    			t.start();
    		}
    	}
    
    	public static void main(String[] args) {
    		JVMStackOOM oom = new JVMStackOOM();
    		oom.stackLeakByThread();
    	}
    }
    

    这段代码会创建出无限多的线程,因为Java的线程会映射系统的内核线程上,所以会造成CPU占用率100%,系统假死等现象,请谨慎运行。在运行之前,设置JVM的参数为-Xss2M,运行很长一段时间后结果如下:

    Exception in thread "main" java.lang.OutMemoryError: unable to create new native thread
    	at java.lang.Thread.start0(Native Method)
    	at java.lang.Thread.start(Unknown Source)
    	at OneMoreStudy.JVMStackOOM.stackLeakByThread(JVMStackOOM.java:18)
    	at OneMoreStudy.JVMStackOOM.main(JVMStackOOM.java:24)
    

    本地方法栈

    本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。

    虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,所以具体的虚拟机可以自由实现它。甚至有的虚拟机(比如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

    Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。它是被所有线程共享的一块内存区域,在虚拟机启动时创建。它就是用来存放对象实例的,几乎所有的对象实例都在这里分配内存。

    堆是垃圾收集器管理的主要区域,如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。从内存分配的角度看,线程共享的堆中又可能划分出多个线程私有的分配缓存区(Thread Local Allocation Buffer,TLAB)。

    根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。

    如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常,再让我们写一段代码,使其抛出该异常:

    /*
     * VM Args: -Xms20M -Xmx20M
     */
    public class HeapOOM {
    	static class OOMObject{
    		
    	}
    	
    	public static void main(String[] args){
    		List<OOMObject> list = new ArrayList<OOMObject>();
    		
    		while(true){
    			//把对象实例放入列表中,
    			//使其一直被引用,不会被垃圾回收
    			list.add(new OOMObject());
    		}
    	}
    }
    

    在运行之前,设置JVM的参数为-Xms20M -Xmx20M,运行结果如下:

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    	at java.util.Arrays.copyOf(Unknown Source)
    	at java.util.Arrays.copyOf(Unknown Source)
    	at java.util.ArrayList.grow(Unknown Source)
    	at java.util.ArrayList.ensureExplicitCapacity(Unknown Source)
    	at java.util.ArrayList.ensureCapacityInternal(Unknown Source)
    	at java.util.ArrayList.add(Unknown Source)
    	at OneMoreStudy.HeapOOM.main(HeapOOM.java:18)
    
    

    方法区

    方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

    对于习惯在HotSpot虚拟机上开发和部署程序的开发者来说,很多人愿意把方法区称为“永久代”(Permanent Generation),本质上两者并不等价,仅仅是因为HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已。在JDK7的HotSpot中,已经把原本在永久代的字符串常量池移出,在JDK8的HotSpot中,已经没有永久代的存在了,而是采用了新的内存空间:元空间(Metaspace)。

    JVM规范对这个区域的限制非常宽松,除了和Java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的,但并不是数据进入了方法区就被一直存放。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说这个区域的回收“成绩”比较难以令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收确实是有必要的。

    根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。再让我们写一段代码,尝试使其抛出该异常:

    /*
     * VM Args: -XX:PermSize=2M -XX:MaxPermSize=2M
     */
    public class RuntimeConstantPoolOOM {
    	public static void main(String[] args) {
    		List<String> list = new ArrayList<String>();
    		for (int i = 0; i < 100000; i++) {
    			System.out.println(i);
    			//将i转化为字符串,
    			//并且调用intern(),把字符串放在运行时常量池
    			list.add(String.valueOf(i).intern());
    		}
    	}
    }
    

    在运行之前,设置JVM的参数为-XX:PermSize=2M -XX:MaxPermSize=2M。

    JDK6中运行抛出了老年代的OutOfMemoryError异常,结果如下:

    ......
    35813
    35814
    Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
            at java.lang.String.intern(Native Method)
            at OneMoreStudy.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:12)
    

    JDK7中运行,循环全部完毕后,也没有抛出任何异常,结果如下:

    ......
    99996
    99997
    99998
    99999
    

    同一段代码,在不同版本JDK中的运行结果为什么是不同的呢?这是因为:在JDK6中,字符串常量池还在永久代中,而在JDK7中,已经把原本在永久代的字符串常量池移出了。

    再再让我们写一段代码,尝试使其抛出该异常:

    /*
     * VM Args: -XX:PermSize=2M -XX:MaxPermSize=2M
     */
    public class MethodAreaOOM {
    	static class OOMObject {
    
    	}
    	
    	public static void main(String[] args) {
    		for (int i = 0; i < 300; i++) {
    			System.out.println(i);
    			createNewClass();
    		}
    	}
    
    	private static void createNewClass() {
    		//这里使用了CGLIB,动态创建类,载入方法区
    		Enhancer enhancer = new Enhancer();
    		enhancer.setSuperclass(OOMObject.class);
    		enhancer.setUseCache(false);
    		enhancer.setCallback(new MethodInterceptor() {
    
    			@Override
    			public Object intercept(Object obj, Method method,
    					Object[] args, MethodProxy proxy) throws Throwable {
    				return proxy.invokeSuper(obj, args);
    			}
    		});
    		enhancer.create();
    	}
    }
    

    在运行之前,设置JVM的参数为-XX:PermSize=2M -XX:MaxPermSize=2M。

    JDK6中运行抛出了老年代的OutOfMemoryError异常,,结果如下:

    ......
    Caused by: java.lang.OutOfMemoryError: PermGen space
            at java.lang.ClassLoader.defineClass1(Native Method)
            at java.lang.ClassLoader.defineClassCond(Unknown Source)
            at java.lang.ClassLoader.defineClass(Unknown Source)
            ... 12 more
    

    JDK7中运行也抛出了OutOfMemoryError异常,结果如下:

    Exception in thread "main"
    Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
    

    JDK8中运行,循环全部完毕后,也没有抛出任何异常,结果如下:

    ......
    298
    299
    Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=2M; support was removed in 8.0
    Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=2M; support was removed in 8.0
    

    同一段代码,在不同版本JDK中的运行结果为什么是不同的呢?这是因为:在JDK6和JDK7中,永久代仍然存在,而在JDK8中,已经没有永久代的存在了,而是采用了新的内存空间:元空间,并且JVM参数PermSize和MaxPermSize也被移出了。

    总结

    在JVM所管理的内存中,大致分为:程序计数器、虚拟机栈、本地方法栈、堆和方法区。程序计数器是当前线程所执行的字节码的行号指示器。虚拟机栈是Java方法执行的内存模型,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。本地方法栈是本地方法执行的内存模型,和虚拟机栈非常相似,其区别是本地方法栈为JVM使用到的Native方法服务。是用于存储对象实例的,是垃圾收集器管理的主要区域。方法区用于存储已被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

    展开全文
  • JVM内存结构

    千次阅读 2021-07-14 21:44:02
    其实 Java 虚拟机的内存结构并不是官方的说法,在《Java 虚拟机规范》中用的是「运行时数据区」这个术语。但很多时候这个名词并不是很形象,再加上日积月累的习惯,我们都习惯用虚拟机内存结构这个说法了 根据《Java...

    概述

    其实 Java 虚拟机的内存结构并不是官方的说法,在《Java 虚拟机规范》中用的是「运行时数据区」这个术语。但很多时候这个名词并不是很形象,再加上日积月累的习惯,我们都习惯用虚拟机内存结构这个说法了
    根据《Java 虚拟机规范》中的说法,Java 虚拟机的内存结构可以分为公有和私有两部分。公有指的是所有线程都共享的部分,指的是 Java 堆、方法区、常量池。私有指的是每个线程的私有数据,包括:PC寄存器、Java 虚拟机栈、本地方法栈。
    在这里插入图片描述
    在这里插入图片描述

    公有部分

    在 Java 虚拟机中,线程共享部分包括

    Java堆

    Java 堆指的是从 JVM 划分出来的一块区域,这块区域专门用于 Java 实例对象的内存分配,几乎所有实例对象都在会这里进行内存的分配。之所以说几乎是因为有特殊情况,有些时候小对象会直接在栈上进行分配,这种现象我们称之为「栈上分配」。这里并不深入介绍,后续有章节会介绍。
    深入说说 Java 堆。

    Java 堆根据对象存活时间的不同,Java 堆还被分为年轻代、老年代两个区域,年轻代还被进一步划分为 Eden 区、From Survivor 0、To Survivor 1 区。如下图所示。
    在这里插入图片描述

    当有对象需要分配时,一个对象永远优先被分配在年轻代的 Eden 区,等到 Eden 区域内存不够时,Java 虚拟机会启动垃圾回收。此时 Eden 区中没有被引用的对象的内存就会被回收,而一些存活时间较长的对象则会进入到老年代。在 JVM 中有一个名为 -XX:MaxTenuringThreshold 的参数专门用来设置晋升到老年代所需要经历的 GC 次数,即在年轻代的对象经过了指定次数的 GC 后,将在下次 GC 时进入老年代。

    这里让我们思考一个问题:为什么 Java 堆要进行这样一个区域划分呢?

    根据我们的经验,虚拟机中的对象必然有存活时间长的对象,也有存活时间短的对象,这是一个普遍存在的正态分布规律。如果我们将其混在一起,那么因为存活时间短的对象有很多,那么势必导致较为频繁的垃圾回收。而垃圾回收时不得不对所有内存都进行扫描,但其实有一部分对象,它们存活时间很长,对他们进行扫描完全是浪费时间。因此为了提高垃圾回收效率,分区就理所当然了。

    另外一个值得我们思考的问题是:为什么默认的虚拟机配置,Eden:from :to = 8:1:1 呢?

    其实这是IBM公司根据大量统计得出的结果。根据IBM公司对对象存活时间的统计,他们发现 80% 的对象存活时间都很短。于是他们将 Eden 区设置为年轻代的 80%,这样可以减少内存空间的浪费,提高内存空间利用率。

    方法区、常量池

    方法区与永久代、元空间(MetaSpace)

    方法区是Java虚拟机规范中的概念,是一种Java虚拟机规范,而永久代,MetaSpace则是对方法区的不同实现。要区分二者的概念。

    方法区指的是存储 Java 类字节码数据的一块区域,它存储了每一个类的结构信息,例如运行时常量池、字段和方法数据、构造方法等。可以看到常量池其实是存放在方法区中的,但《Java 虚拟机规范》将常量池和方法区放在同一个等级上。

    方法区在不同版本的虚拟机有不同的表现形式,例如在 1.7 版本的 HotSpot 虚拟机中,方法区被称为永久代(Permanent Space),而在 JDK 1.8 中则被称之为 MetaSpace。

    字符串常量池

    并不是所有的常量池都在方法区中,因为自JDK 7起,原本存放在永久代的字符串常量池被移至Java堆之中,在JDK1.8环境下运行一下代码并不会导致方法区溢出(String::intern()是一个本地方法,它的作用是如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象的引用;否则,会将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用)

    public class RuntimeConstantPoolOOM_1 {
        public static void main(String[] args) {
            // 使用Set保持着常量池引用,避免Full GC回收常量池行为
            Set<String> set = new HashSet<String>();
            // 在short范围内足以让6MB的PermSize产生OOM了
            short i = 0;
            while (true) {
                set.add(String.valueOf(i++).intern());
            }
        }
    }
    

    相反,使用-Xmx参数限制最大堆到6MB就能够看到
    -Xms6m -Xmx6m -Xss128k -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=E:\heapdump

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    	at java.util.HashMap.newNode(HashMap.java:1750)
    	at java.util.HashMap.putVal(HashMap.java:631)
    	at java.util.HashMap.put(HashMap.java:612)
    	at java.util.HashSet.add(HashSet.java:220)
    	at jvm.RuntimeConstantPoolOOM_1.main(RuntimeConstantPoolOOM_1.java:17)
    

    说明JDK1.8把字符串常量池放在堆中

    私有部分

    Java 堆以及方法区的数据是共享的,但是有一些部分则是线程私有的。线程私有部分可以分为:PC 寄存器、Java 虚拟机栈、本地方法栈三大部分。

    PC寄存器(程序计数器)

    程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
    在Java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

    注意:
    如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)。此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域

    为什么需要程序计数器 ?

    由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器。

    Java 虚拟机栈

    在这里插入图片描述

    Java 虚拟机栈,这个栈与线程同时创建,命周期与程序计数器相同,就是我们平时说的栈,用来存储栈帧,即存储局部变量与一些过程结果的地方。栈帧存储的数据包括:局部变量表、操作数栈等信息。

    虚拟机栈-过程详情描述

    ①每创建一个线程,虚拟机就会为这个线程创建一个虚拟机栈,
    ②虚拟机栈表示Java方法执行的内存模型,每调用一个方法,就会生成一个栈帧(Stack Frame)用于存储方法的本地变量表、操作栈、方法出口等信息,当这个方法执行完后,就会弹出相应的栈帧。

    本地方法栈

    当 Java 虚拟机使用其他语言(例如 C 语言)来实现指令集解释器时,也会使用到本地方法栈。如果 Java 虚拟机不支持 natvie 方法,并且自己也不依赖传统栈的话,可以无需支持本地方法栈。

    直接内存

    直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现,所以我们放到这里一起讲解。

    参考文章:
    【1】深入理解Java虚拟机(第3版)
    【2】https://www.cnblogs.com/chanshuyi/p/jvm_serial_06_jvm_memory_model.html
    【3】https://blog.csdn.net/weixin_45393094/article/details/106163605

    展开全文
  • Oracle数据库的体系结构包括四个方面:数据库的物理结构、逻辑结构、内存结构及进程。  1. 物理结构   物理数据库结构是由构成数据库的操作系统文件所决定,Oracle数据库文件包括:  1.1.数据文件(Data File) ...

    Oracle数据库的体系结构包括四个方面:数据库的物理结构、逻辑结构、内存结构及进程。  

    1. 物理结构  

       物理数据库结构是由构成数据库的操作系统文件所决定,Oracle数据库文件包括:  
    1.1.数据文件(Data File) 
       数据文件用来存储数据库中的全部数据,例如数据库表中的数据和索引数据.通常以为*.dbf格式,例如:userCIMS.dbf 。
    1.2.日志文件(Redo Log File) 
       日志文件用于记录数据库所做的全部变更(如增加、删除、修改)、以便在系统发生故障时,用它对数据库进行恢复。名字通常为Log*.dbf格式,如:Log1CIMS.dbf,Log2CIMS.dbf 。  
    1.3.控制文件(Control File) 
        每个Oracle数据库都有相应的控制文件,它们是较小的二进制文件,用于记录数据库的物理结构,如:数据库名、数据库的数据文件和日志文件的名字和位置等信息。用于打开、存取数据库。名字通常为Ctrl*ctl 格式,如Ctrl1CIMS.ctl。  
    1.4.配置文件 
       配置文件记录Oracle数据库运行时的一些重要参数,如:数据块的大小,内存结构的配置等。名字通常为init*.ora 格式,如:initCIMS.ora 。  

    2 逻辑结构  

      Oracle数据库的逻辑结构描述了数据库从逻辑上如何来存储数据库中的数据。逻辑结构包括表空间、段、区、数据块和模式对象。数据库的逻辑结构将支配一个数据库如何使用系统的物理空间.模式对象及其之间的联系则描述了关系数据库之间的设计.  
      一个数据库从逻辑上说是由一个或多个表空间所组成,表空间是数据库中物理编组的数据仓库,每一个表空间是由段(segment)组成,一个段是由一组区(extent)所组成,一个区是由一组连续的数据库块(database block)组成,而一个数据库块对应硬盘上的一个或多个物理块。一个表空间存放一个或多个数据库的物理文件(即数据文件).一个数据库中的数据被逻辑地存储在表空间上。  
    2.1表空间(tablespace) 
      Oracle数据库被划分为一个或多个称为表空间的逻辑结构,它包括两类表空间,System表空间和非System表空间,其中,System表空间是安装数据库时自动建立的,它包含数据库的全部数据字典,存储过程、包、函数和触发器的定义以及系统回滚段。除此之外,还能包含用户数据。。
      一个表空间包含许多段,每个段有一些可以不连续的区组成,每个区由一组连续的数据块组成,数据块是数据库进行操作的最小单位。  
      每个表空间对应一个或多个数据文件,每个数据文件只能属于一个表空间。  
    2.2.数据库块(database block) 
      数据库块也称逻辑块或ORACLE块,它对应磁盘上一个或多个物理块,它的大小由初始化参数db-block-size(在文件init.ora中)决定,典型的大小是2k。Pckfree 和pctused 两个参数用来优化数据块空间的使用。  
    2.3.区(extent) 
      区是由一组连续的数据块所组成的数据库存储空间分配的逻辑单位。  
    2.4.段(segment) 
      段是一个或多个不连续的区的集合,它包括一个表空间内特定逻辑结构的所有数据,段不能跨表空间存放。Oracle数据库包括数据段、索引段、临时段、回滚段等。  
    2.5.模式对象(schema object) 
      Oracle数据库的模式对象包括表、视图、序列、同意词、索引、触发器、存储.过程等,关于它们将重点在后面章节介绍。  

    3.Oracle Server系统进程与内存结构  

      当在计算机服务器上启动Oracle数据库后,称服务器上启动了一个Oracle实例(Instance)。ORACLE 实例(Instance)是存取和控制数据库的软件机制,它包含系统全局区(SGA)和ORACLE进程两部分。SGA是系统为实例分配的一组共享内存缓冲区,用于存放数据库实例和控制信息,以实现对数据库中数据的治理和操作。  
      进程是操作系统中一个极为重要的概念。一个进程执行一组操作,完成一个特定的任务.对ORACLE数据库治理系统来说,进程由用户进程、服务器进程和后台进程所组成。  
      当用户运行一个应用程序时,系统就为它建立一个用户进程。服务器进程处理与之相连的用户进程的请求,它与用户进程相通讯,为相连的用户进程的ORACLE请求服务。  
      为了提高系统性能,更好地实现多用户功能,ORACLE还在系统后台启动一些后台进程,用于数据库数据操作。  

    4.系统进程的后台进程主要包括:

      SMON 系统监控进程:(system monitor)负责完成自动实例恢复和回收分类(sort)表空间。 
      PMON 进程监控进程:(PRocess monitor)实现用户进程故障恢复、清理内存区和释放该进程所需资源等。 
      DBWR 数据库写进程:数据库缓冲区的治理进程。
    在它的治理下,数据库缓冲区中总保持有一定数量的自由缓冲块,以确保用户进程总能找到供其使用的自由缓冲块。 
      LGWR 日志文件写进程:是日志缓冲区的治理进程,负责把日志缓冲区中的日志项写入磁盘中的日志文件上。每个实例只有一个LGWR进程。
      ARCH 归档进程:(archiver process)把已经填满的在线日志文件拷贝到一个指定的存储设备上。仅当日志文件组开关(switch)出现时,才进行ARCH操作。ARCH不是必须的,而只有当自动归档可使用或者当手工归档请求时才发出。
      RECO 恢复进程:是在具有分布式选项时使用的一个进程,主要用于解决引用分布式事务时所出现的故障。它只能在答应分布式事务的系统中出现。
      LCKn 封锁进程:用于并行服务器系统,主要完成实例之间的封锁。 

    5.内存结构(SGA)

      SGA是Oracle为一个实例分配的一组共享内存缓冲区,它包含该实例的数据和控制信息。SGA在实例启动时被自动分配,当实例关闭时被收回。数据库的所有数据操作都要通过SGA来进行。  
      SGA中内存根据存放信息的不同,可以分为如下几个区域:
    5.1.Buffer Cache:存放数据库中数据库块的拷贝。
        它是由一组缓冲块所组成,这些缓冲块为所有与该实例相链接的用户进程所共享。缓冲块的数目由初始化参数DB_BLOCK_BUFFERS确定,缓冲块的大小由初始化参数DB_BLOCK_SIZE确定。大的数据块可提高查询速度。它由DBWR操作。

    5.2.日志缓冲区Redo Log Buffer:存放数据操作的更改信息。
        它们以日志项(redo entry)的形式存放在日志缓冲区中。当需要进行数据库恢复时,日志项用于重构或回滚对数据库所做的变更。日志缓冲区的大小由初始化参数LOG_BUFFER确定。大的日志缓冲区可减少日志文件I/O的次数。后台进程LGWR将日志缓冲区中的信息写入磁盘的日志文件中,可启动ARCH后台进程进行日志信息归档。
      
    5.3.共享池Shared Pool:包含用来处理的SQL语句信息。
        它包含共享SQL区和数据字典存储区。共享SQL区包含执行特定的SQL语句所用的信息。数据字典区用于存放数据字典,它为所有用户进程所共享。
      
    5.4.Cursors
    :一些内存指针,执行待处理的SQL语句 
    5.5.其他信息区:除了上述几个信息区外,还包括一些进程之间的通讯信息(如封锁信息);在多线索服务器配置下,还有一些程序全局区的信息,请求队列和响应队列等。

    --------------------------------------------------------------------------

    后台进程详解

    oracle中的进程共分为三类:用户进程、服务进程、后台进程。其中后台进程伴随实例的启动而启动,他们主要是维护数据库的稳定,相当于一个企业中的管理者及内部服务人员。他们并不会直接给用户提供服务。
    一:database write(DBWn(n代表数字1..n,表示可能有几个DBW在运行) )--数据写入
       作用:把SGA中被修改的数据同步到磁盘文件中。保证Buffer Cache中有足够的空闲数据块数量。
       触发条件: 1、检查点
                 2、一个服务进程在设定的时间内没有找到空闲块
                 3、每三秒自动唤醒一次。
       设置:DB_WRITER_PROCESS用来定义DBWn进程数量。(commit命令只是把记录修改写入日志文件,不是把修改后的数据写入数据文件)

    二:log write(LGWR)--日志文件写入
       作用:把log buffer中的日志内容写入联机的日志文件中,释放log用户buffer空间。
       触发条件:1、用户发出commit命令。(在oracle中称为快速提交机制(fast commit)):把redo log buffer中的记录写入日志文件,写入一条提交的记录
                2、三秒定时唤醒。
                3、log buffer超过1/3,或日志数量超过1M。
                4、DBWR进程触发:DBWn视图将脏数据块写入磁盘先检测他的相关redo记录是否写入联机日志文件,如果没有就通知LGWR进程。在oracle中成为提前写机制(write ahead):redo记录先于数据记录被写入磁盘

    三:checkpoint(CKPT)-检查点事件
       作用:维护数据库一致性状态。检测点时刻数据文件与SGA中的内容一致,这不是一个单独的进程,要和前两个进程一起工作呦。DBWR写入脏数据,同时触发LGWR进程。
          CKPT更新控制文件中的检查点记录。
       触发条件:日志切换(log switch)会触发检查点。

    四:process monitor(PMON)--进程监控
       作用:1、发现用户进程异常终止,并进行清理。释放占用资源。(清理异常终止用户使用的锁)
            2、向监听程序动态的注册实例。
       触发条件:定时被唤醒,其他进程也会主动唤醒它。

    五:system monitor:(SMON)--系统监控
       作用:1、负责实例恢复,前滚(Roll Forward)恢复到实例关闭的状态,使用最后一次检查点后的日志进程重做。这时包括提交和未提交的事务。打开数据库,进行回滚(Roll Back):回滚未提交的事务。(oracle承诺commit之后数据不会丢失,现在我们可以大致的了解是如何实现这个承诺,以及在数据的安全性和数据库性能之间的平衡选择。)
            2、负责清理临时段,以释放空间
       触发条件:定期被唤醒或者被其他事务主动唤醒。

    六:archive:(ARCn)--归档操作
       作用:发生日志切换时把写满的联机日志文件拷贝到归档目录中。
       触发条件:日志切换时被LGWR唤醒。
       设置:LOG_ARCHIVE_MAX_PROCESSES可以设置oracle启动的时候ARCn的个数。

    七:manageability monitor:MMON--AWR主要的进程
       作用:1、收集AWR必须的统计数据,把统计数据写入磁盘。10g中保存在SYSAUX表空间中。
            2、生成server--generated报警
       每小时把shared pool中的统计信息写入磁盘,或者shared pool占用超过15%。

    八:manageability monitor light:MMNL:轻量级的MMON

    九:memory manager:MMAN:自动内容管理
       作用:每分钟都检查AWR性能信息,并根据这些信息来决定SGA组件最佳分布。
       设置:STATISTICS_LEVEL:统计级别
            SGA_TARGET:SGA总大小

    十:job queue coordinator:CJQ0--数据库定时任务

    十一:recover writer:RVWR
       作用:为flashback database提供日志记录。把数据块的前镜像写入日志。

    十二:change tarcking writer:CTWR
       作用:跟踪数据块的变化,把数据块地址记录到 change_tracking file文件中。RMAN的增量备份将使用这个文件来确定那些数据块发生了变化,并进行备份。

    以上是oracle中比较常见的后台进程,不是所有的进程。其中DBWn,ARCn可以设置数据库启动时的数量。这些进程在系统中可以不止一个。


    
    








    展开全文
  • 比如本文我们要讨论的JVM内存结构、JAVA内存结构、JAVA内存区域、Java内存模型,这就是几个截然不同的概念,但是很多人容易弄混。 可以这样说,很多高级开发甚至都搞不不清楚JVM内存结构、JAVA内存结构、JAVA内存...

    Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点。而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚。比如本文我们要讨论的JVM内存结构、JAVA内存结构、JAVA内存区域Java内存模型,这就是几个截然不同的概念,但是很多人容易弄混。

    可以这样说,很多高级开发甚至都搞不不清楚JVM内存结构、JAVA内存结构、JAVA内存区域Java内存模型这几个的概念及其间的区别。甚至我见过有些面试官自己也搞的不是太清楚。不信的话,你去网上搜索Java内存模型,还会有很多文章的内容其实介绍的是JVM内存结构。

    前提:

    本文讲的基本都是以Sun HotSpot虚拟机为基础的,Oracle收购了Sun后目前得到了两个【Sun的HotSpot和JRockit(以后可能合并这两个),还有一个是IBM的IBMJVM】

    前一篇文章 编程的基础知识:CPU和内存的交互以及volatile型变量的理解

    之所以扯了那么多计算机内存模型,是因为java内存模型的设定符合了计算机的规范。

    JVM内存结构、Java内存结构、Java内存区域

    这三者都是一个概念

    我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途。其中有些区域随着虚拟机进程的启动而存在,而有些区域则依赖用户线程的启动和结束而建立和销毁。

    JVM运行时内存结构(也就是咱们常说的运行时数据区)

    五大内存区域

    程序计数器

    程序计数器是一块很小的内存空间,它是线程私有的,可以认作为当前线程的行号指示器。

    为什么需要程序计数器

    我们知道对于一个处理器(如果是多核cpu那就是一核),在一个确定的时刻都只会执行一条线程中的指令,一条线程中有多个指令,为了线程切换可以恢复到正确执行位置,每个线程都需有独立的一个程序计数器,不同线程之间的程序计数器互不影响,独立存储。

    注意:如果线程执行的是个java方法,那么计数器记录虚拟机字节码指令的地址。如果为native【底层方法】,那么计数器为空。这块内存区域是虚拟机规范中唯一没有OutOfMemoryError的区域

    Java栈(虚拟机栈)

    同计数器也为线程私有,生命周期与相同,就是我们平时说的栈,栈描述的是Java方法执行的内存模型

    每个方法被执行的时候都会创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。【栈先进后出,下图栈1先进最后出来】

    对于栈帧的解释参考 Java虚拟机运行时栈帧结构

    栈帧: 是用来存储数据和部分过程结果的数据结构。
    栈帧的位置:  内存 -> 运行时数据区 -> 某个线程对应的虚拟机栈 -> here[在这里]
    栈帧大小确定时间: 编译期确定,不受运行期数据影响。
    

    通常有人将java内存区分为栈和堆,实际上java内存比这复杂,这么区分可能是因为我们最关注,与对象内存分配关系最密切的是这两个。

    平时说的栈一般指局部变量表部分。

    局部变量表:一片连续的内存空间,用来存放方法参数,以及方法内定义的局部变量,存放着编译期间已知的数据类型(八大基本类型和对象引用(reference类型),returnAddress类型。它的最小的局部变量表空间单位为Slot,虚拟机没有指明Slot的大小,但在jvm中,long和double类型数据明确规定为64位,这两个类型占2个Slot,其它基本类型固定占用1个Slot。

    reference类型:与基本类型不同的是它不等同本身,即使是String,内部也是char数组组成,它可能是指向一个对象起始位置指针,也可能指向一个代表对象的句柄或其他与该对象有关的位置。

    returnAddress类型:指向一条字节码指令的地址【深入理解Java虚拟机】怎么理解returnAddress

    需要注意的是,局部变量表所需要的内存空间在编译期完成分配,当进入一个方法时,这个方法在栈中需要分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表大小。

    Java虚拟机栈可能出现两种类型的异常:

    1. 线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError。
    2. 虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出OutOfMemory异常。

    本地方法栈

    本地方法栈是与虚拟机栈发挥的作用十分相似,区别是虚拟机栈执行的是Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的native方法服务,可能底层调用的c或者c++,我们打开jdk安装目录可以看到也有很多用c编写的文件,可能就是native方法所调用的c代码。

    对于大多数应用来说,堆是java虚拟机管理内存最大的一块内存区域,因为堆存放的对象是线程共享的,所以多线程的时候也需要同步机制。因此需要重点了解下。

    java虚拟机规范对这块的描述是:所有对象实例及数组都要在堆上分配内存,但随着JIT编译器的发展和逃逸分析技术的成熟,这个说法也不是那么绝对,但是大多数情况都是这样的。

    即时编译器:可以把把Java的字节码,包括需要被解释的指令的程序)转换成可以直接发送给处理器的指令的程序)

    逃逸分析:通过逃逸分析来决定某些实例或者变量是否要在堆中进行分配,如果开启了逃逸分析,即可将这些变量直接在栈上进行分配,而非堆上进行分配。这些变量的指针可以被全局所引用,或者其其它线程所引用。

    参考逃逸分析

    注意:它是所有线程共享的,它的目的是存放对象实例。同时它也是GC所管理的主要区域,因此常被称为GC堆,又由于现在收集器常使用分代算法,Java堆中还可以细分为新生代和老年代,再细致点还有Eden(伊甸园)空间之类的不做深究。

    根据虚拟机规范,Java堆可以存在物理上不连续的内存空间,就像磁盘空间只要逻辑是连续的即可。它的内存大小可以设为固定大小,也可以扩展。

    当前主流的虚拟机如HotPot都能按扩展实现(通过设置 -Xmx和-Xms),如果堆中没有内存内存完成实例分配,而且堆无法扩展将报OOM错误(OutOfMemoryError)

    方法区

    方法区同堆一样,是所有线程共享的内存区域,为了区分堆,又被称为非堆。

    用于存储已被虚拟机加载的类信息、常量、静态变量,如static修饰的变量加载类的时候就被加载到方法区中。

    运行时常量池

    是方法区的一部分,class文件除了有类的字段、接口、方法等描述信息之外,还有常量池用于存放编译期间生成的各种字面量和符号引用。

    在老版jdk,方法区也被称为永久代【因为没有强制要求方法区必须实现垃圾回收,HotSpot虚拟机以永久代来实现方法区,从而JVM的垃圾收集器可以像管理堆区一样管理这部分区域,从而不需要专门为这部分设计垃圾回收机制。不过自从JDK7之后,Hotspot虚拟机便将运行时常量池从永久代移除了。】

    jdk1.7开始逐步去永久代。从String.interns()方法可以看出来 String.interns()

    native方法:

    作用是如果字符串常量池已经包含一个等于这个String对象的字符串,则返回代表池中的这个字符串的String对象,在jdk1.6及以前常量池分配在永久代中。

    可通过 -XX:PermSize和-XX:MaxPermSize限制方法区大小。

    public class StringIntern {
        //运行如下代码探究运行时常量池的位置
        public static void main(String[] args) throws Throwable {
            //用list保持着引用 防止full gc回收常量池
            List<String> list = new ArrayList<String>();
            int i = 0;
            while(true){
                list.add(String.valueOf(i++).intern());
            }
        }
    }
    //如果在jdk1.6环境下运行 同时限制方法区大小 将报OOM后面跟着PermGen space说明方法区OOM,即常量池在永久代
    //如果是jdk1.7或1.8环境下运行 同时限制堆的大小  将报heap space 即常量池在堆中

    idea设置相关内存大小设置

    这边不用全局的方式,设置main方法的vm参数。

    做相关设置,比如说这边设定堆大小。(-Xmx5m -Xms5m -XX:-UseGCOverheadLimit)

    jdk8真正开始废弃永久代,而使用元空间(Metaspace)

    java虚拟机对方法区比较宽松,除了跟堆一样可以不存在连续的内存空间,定义空间和可扩展空间,还可以选择不实现垃圾收集。

    总结

    这里简单提几个需要特别注意的点:

    1、以上是Java虚拟机规范,不同的虚拟机实现会各有不同,但是一般会遵守规范。

    2、规范中定义的方法区,只是一种概念上的区域,并说明了其应该具有什么功能。但是并没有规定这个区域到底应该处于何处。所以,对于不同的虚拟机实现来说,是由一定的自由度的。

    3、不同版本的方法区所处位置不同,上图中划分的是逻辑区域,并不是绝对意义上的物理区域。因为某些版本的JDK中方法区其实是在堆中实现的。

    4、运行时常量池用于存放编译期生成的各种字面量和符号应用。但是,Java语言并不要求常量只有在编译期才能产生。比如在运行期,String.intern也会把新的常量放入池中。

    5、除了以上介绍的JVM运行时内存外,还有一块内存区域可供使用,那就是直接内存。Java虚拟机规范并没有定义这块内存区域,所以他并不由JVM管理,是利用本地方法库直接在堆外申请的内存区域。

    6、堆和栈的数据划分也不是绝对的,如HotSpot的JIT会针对对象分配做相应的优化。

    如上,做个总结,JVM内存结构,由Java虚拟机规范定义。描述的是Java程序执行过程中,由JVM管理的不同数据区域。各个区域有其特定的功能。

    Java内存模型

    Java程序内存的分配是在JVM虚拟机内存分配机制下完成

    Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。

    简要言之,jmm是jvm的一种规范,定义了jvm的内存模型。它屏蔽了各种硬件和操作系统的访问差异,不像c那样直接访问硬件内存,相对安全很多,它的主要目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。可以保证并发编程场景中的原子性、可见性和有序性。

    Java内存模型看上去和Java内存结构(JVM内存结构)差不多,很多人会误以为两者是一回事儿,这也就导致面试过程中经常答非所为。

    在前面的关于JVM的内存结构的图中,我们可以看到,其中Java堆和方法区的区域是多个线程共享的数据区域。也就是说,多个线程可能可以操作保存在堆或者方法区中的同一个数据。这也就是我们常说的“Java的线程间通过共享内存进行通信”。

    Java内存模型是根据英文Java Memory Model(JMM)翻译过来的。其实JMM并不像JVM内存结构一样是真实存在的。他只是一个抽象的概念。JSR-133: Java Memory Model and Thread Specification中描述了,JMM是和多线程相关的,他描述了一组规则或规范,这个规范定义了一个线程对共享变量的写入时对另一个线程是可见的。

    那么,简单总结下,Java的多线程之间是通过共享内存进行通信的,而由于采用共享内存进行通信,在通信过程中会存在一系列如可见性、原子性、顺序性等问题,而JMM就是围绕着多线程通信以及与其相关的一系列特性而建立的模型。JMM定义了一些语法集,这些语法集映射到Java语言中就是volatile、synchronized等关键字。

    在Java中,JMM是一个非常重要的概念,正是由于有了JMM,Java的并发编程才能避免很多问题。这里就不对Java内存模型做更加详细的介绍了,想了解更多的朋友可以参考《Java并发编程的艺术》。

    内存溢出

    两种内存溢出异常[注意内存溢出是error级别的]
    1.StackOverFlowError:当请求的栈深度大于虚拟机所允许的最大深度
    2.OutOfMemoryError:虚拟机在扩展栈时无法申请到足够的内存空间[一般都能设置扩大]
    

    java -verbose:class -version 可以查看刚开始加载的类,可以发现这两个类并不是异常出现的时候才去加载,而是jvm启动的时候就已经加载。这么做的原因是在vm启动过程中我们把类加载起来,并创建几个没有堆栈的对象缓存起来,只需要设置下不同的提示信息即可,当需要抛出特定类型的OutOfMemoryError异常的时候,就直接拿出缓存里的这几个对象就可以了。

    比如说OutOfMemoryError对象,jvm预留出4个对象【固定常量】,这就为什么最多出现4次有堆栈的OutOfMemoryError异常及大部分情况下都将看到没有堆栈的OutOfMemoryError对象的原因。

    参考OutOfMemoryError解读

    两个基本的例子

    public class MemErrorTest {
        public static void main(String[] args) {
            try {
                List<Object> list = new ArrayList<Object>();
                for(;;) {
                    list.add(new Object()); //创建对象速度可能高于jvm回收速度
                }
            } catch (OutOfMemoryError e) {
                e.printStackTrace();
            }
    
            try {
                //递归造成StackOverflowError 这边因为每运行一个方法将创建一个栈帧,
                //栈帧创建太多无法继续申请到内存扩展
                hi();
            } catch (StackOverflowError e) {
                e.printStackTrace();
            }
    
        }
    
        public static void hi() {
            hi();
        }
    }

     

    参考资料:

    JVM内存结构、Java内存模型以及Java对象模型之间的区别:https://blog.csdn.net/ZYC88888/article/details/83756364

    深入理解JVM-内存模型(jmm)和GC:https://www.jianshu.com/p/76959115d486

    展开全文
  • JVM之内存结构详解

    万次阅读 多人点赞 2019-10-18 12:49:05
    对于开发人员来说,如果不了解Java的JVM,那真的是很难写得一手好代码,很难查得一手好bug。同时,JVM也是面试环节的中重灾区。...下面,开启我们的第一篇文章《JVM之内存结构详解》。 学习也是要讲究方式方法...
  • 内存篇:JVM内存结构

    万次阅读 2019-11-01 19:50:24
    Java8相对之前的版本,JVM结构发生了较大的变化,取消了永久代,新增了元空间,...JVM内存结构1 JVM结构详解1.1 堆(Heap)1.2 方法区(Method Area)1.3 程序计数器(Program Counter Register)1.4 虚拟机栈(JVM ...
  • Java对象内存结构及大小计算

    千次阅读 2016-11-21 14:21:54
    程序员杂谈 2016-11-19 21:15 java编程中处处离不开...内存结构 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。如下图所示:
  • JVM运行时内存结构

    千次阅读 2019-02-26 08:20:52
    主要结构:堆内存、方法区、栈(说明:基于JDK1.7) 堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,默认情况下年轻代按照8:1:1的比例...
  • JVM内存结构详解

    千次阅读 2020-03-20 19:13:09
    你是否遇到这样的困惑:堆内存该设置多大?OutOfMemoryError异常到底是怎么引起的?...JVM内存结构 java虚拟机在执行程序的过程中会将内存划分为不同的数据区域,看一下下图。 如果理解了上图...
  • 一文搞懂JVM内存结构

    万次阅读 多人点赞 2019-04-11 20:30:23
    然而你对JVM的内存结构都不清楚,就妄想解决JVM引发的故障问题,是不切实际的。只有基础打好了,对于JVM故障问题才能“披荆斩棘”。本文通过代码与图示详细讲解了JVM内存区域,相信阅读本文之后,你将对JVM内存的堆...
  • 深入理解JVM的内存结构及GC机制

    万次阅读 多人点赞 2017-11-23 02:12:38
    一、前言 JAVA GC(Garbage Collection,垃圾回收)机制是区别C++的一个重要特征,C++需要开发者自己...但这并不意味着我们不用去理解GC机制的原理,因为如果不了解其原理,可能会引发内存泄漏、频繁GC导致应用卡顿
  • JVM内存结构和Java内存模型别再傻傻分不清了

    万次阅读 多人点赞 2020-03-04 20:53:30
    JVM内存结构和Java内存模型都是面试的热点问题,名字看感觉都差不多,网上有些博客也都把这两个概念混着用,实际上他们之间差别还是挺大的。 通俗点说,JVM内存结构是与JVM的内部存储结构相关,而Java内存模型是与多...
  • 计算机内存结构

    千次阅读 2016-11-29 21:53:16
    变量的三大特点: 数据类型、作用域、存储类型 计算机的三大组成:  1.CPU  2.内存  3.硬盘 内存与硬盘的区别:  1.内存中保存动态数据,硬盘中保存静态数据。  2.断电后内存中的数据丢失,硬盘中的数据不会丢失...
  • oracle 内存结构详解

    千次阅读 2014-10-17 09:20:17
    Oracle 内存结构 与 Oracle 实例关联的基本内存结构包括: 系统全局区 (SGA):由所有服务器和后台进程共享。SGA 中存储的数据示例包括高速缓存的数据块和共享 SQL 区域。 程序全局区 (PGA):各个服务器...
  • Oracle内存结构

    千次阅读 2018-07-26 17:26:22
    即,Oracle数据库由一系列后台进程、内存及文件组成。 当启动一个数据库时,Oracle会分配一个被称为系统全局区SGA的共享内存区,同时会启动多个后台进程,他们共同构成了一个Oracle实例。 2.Oracle内存 2.1 ...
  • JAVA的内存结构

    万次阅读 多人点赞 2018-08-01 09:41:28
    JVM所管理的内存包括以下几个运行时数据区域,如图所示 方法区和堆为线程共享区,虚拟机栈、本地方法栈程序计数器为线程独占区。 程序计数器 程序计数器是一块较小的空间,它可以看作是当前...
  • jvm内存结构和内存模型

    千次阅读 2019-09-04 17:42:46
    JVM内存结构 《深入理解Java虚拟机(第2版)》中的描述是下面这个样子的: JVM的内存结构大概分为: 堆(Heap):线程共享。所有的对象实例以及数组都要在堆上分配。回收器主要管理的对象。 方法区(Method ...
  • 最简单的JVM内存结构

    千次阅读 2021-03-11 20:42:03
    JVM内存结构图 方法区 堆 栈 程序计数器 本地方法栈 直接内存 内存分配性能优化-逃逸分析 总结 JVM内存结构图 大家好,好几天没有更新了,今天的内容有点多,我们详细介绍下JVM内部结构图,还是和之前...
  • [精]Oracle 内存结构详解

    万次阅读 2016-04-29 15:42:45
    内存结构现代计算机中,CPU 对内存的访问速度要比从磁盘的速度快千倍,因此 Oracle 对于数据的访问 也尽量都在内存中完成,而不是直接修改硬盘上的数据。内存内容在合适的时候再同步到磁盘。 Oracle 利用内存来克服...
  •  我们在运行程序时,有时会碰到内存溢出(OutOfMemoryError)的问题,为了解决这种问题,我们有必要了解JVM的内存结构和垃圾回收机制。 目录:  1.JVM内存结构  2.JVM垃圾回收  3.解决堆溢出问题 正文:  1....
  • 【JVM】——内存结构

    千次阅读 2018-08-29 09:15:25
    二、Java的内存结构: JVM基本结构图: JVM内存结构主要有三大块:堆内存、方法区和栈。 1.堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To ...
  • (3) Java内存结构

    千次阅读 2016-08-02 15:49:37
    Java的运行时数据区,也就是Java的内存结构(Java Structure),但注意区分Java的内存模型(Java Memory Model,JMM),JMM现在我还没仔细去研究过,有兴趣的同学可以自行百度查看下,如果以后有时间,我也会研究,...
  • 这一次,终于系统的学习了 JVM 内存结构

    千次阅读 多人点赞 2019-11-05 11:14:39
    最近在看《 JAVA并发编程实践 》这本书,里面涉及到了 Java 内存模型,通过 Java 内存模型顺理成章的来到的 JVM 内存结构,关于 JVM 内存结构的认知还停留在上大学那会的课堂上,一直没有系统的学习这一块的知识,...
  • Java内存结构是每个java程序员必须掌握理解的,这是Java的核心基础,对我们编写代码特别是并发编程时有很大帮助。由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分。 ...
  • Oracle 内存结构和进程结构

    千次阅读 2010-01-15 21:16:00
    Oracle 内存结构如下图:1:实例(Instance) 在一个中,每一个运行的Oracle数据库都与一个数据库实例相联系,实例是我们 访问数据库的手段。 实例在操作系统中用ORACLE_SID来标识,在Oracle中用参数INSTANCE_...
  • C++类的存储类对象内存结构

    万次阅读 多人点赞 2016-07-22 16:06:12
    本文分两部分,前半部分讲类的存储后半部分讲类的内存结构。 C++类的存储 c++中最重要的就是类,那么一个类的对象,它在内存中如何存储的?它占 内存中多少个字节? 首先确定类的构成: 1,数据成员:可以是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 640,412
精华内容 256,164
关键字:

内存的结构及作用