Java内存模型

Java内存模型
Java内存模型

Java内存模型

2016-05-27朱小厮

↑点击上方"程序人生"关注我

Question:在并发编程中,多个线程之间采取什么机制进行通信(信息交换),什么机制进行数据的同步?

Answer:在Java语言中,采用的是共享内存模型来实现多线程之间的信息交换和数据同步的。

线程之间通过共享程序公共的状态,通过读-写内存中公共状态的方式来进行隐式的通信。同步指的是程序在控制多个线程之间执行程序的相对顺序的机制,在共享内存模型中,同

步是显式的,程序员必须显式指定某个方法/代码块需要在多线程之间互斥执行。

概述

Java内存模型的主要目标是定义程序中各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取出变量这样的底层细节。此处的变量与Java编程里面的变量有所不同,它包含了实例字段、静态字段和构成数组对象的元素,但不包含局部变量和方法参数,因为后者是线程私有的,不会共享,当然不存在数据竞争问题(如果局部变量是一个reference引用类型,它引用的对象在Java堆中可被各个线程共享,但是reference引用本身在Java栈的局部变量表中,是线程私有的)。为了获得较高的执行效能,Java内存模型并没有限制执行引起使用处理器的特定寄存器或者缓存来和主内存进行交互,也没有限制即时编译器进行调整代码执行顺序这类优化措施。

JMM规定了所有的变量都存储在主内存(Main Memory)中。每个线程还有自己的工作内存(Working Memory),线程的工作内存中保存了该线程使用到的变量的主内存的副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(volatile变量仍然有工作内存的拷贝,但是由于它特殊的操作顺序性规定,所以看起来如同直接在主内存中读写访问一般)。不同的线程之间也无法直接访问对方工作内存中的变量,线程之间值的传递都需要通过主内存来完成。

线程1和线程2要想进行数据的交换一般要经历下面的步骤:

1.线程1把工作内存1中的更新过的共享变量刷新到主内存中去。

2.线程2到主内存中去读取线程1刷新过的共享变量,然后copy一份到工作内存2中

去。

内存模型的特性

Java内存模型是围绕着并发编程中原子性、可见性、有序性这三个特征来建立的,那我们依次看一下这三个特征:

原子性(Atomicity)

原子性是指一个操作不能被打断,要么全部执行完毕,要么不执行。在这点上有点类似于事务操作,要么全部执行成功,要么回退到执行该操作之前的状态。

基本类型数据的访问大都是原子操作,long 和double类型的变量是64位,但是在32位JVM中,32位的JVM会将64位数据的读写操作分为2次32位的读写操作来进行,这就导致了long、double类型的变量在32位虚拟机中是非原子操作,数据有可能会被破坏,也就意味着多个线程在并发访问的时候是线程非安全的。

下面我们来演示这个32位JVM下,对64位long类型的数据的访问的问题:

【代码1】

public class NotAtomicity {

//静态变量t

public static long t = 0;

//静态变量t的get方法

public static long getT() {

return t;

}

//静态变量t的set方法

public static void setT(long t) {

NotAtomicity.t = t;

}

//改变变量t的线程

public static class ChangeT implements Runnable{

private long to;

public ChangeT(long to) {

this.to = to;

}

public void run() {

//不断的将long变量设值到 t中

while (true) {

NotAtomicity.setT(to);

//将当前线程的执行时间片段让出去,以便由线程调度机制重新决定哪个线程可以执行

Thread.yield();

}

}

}

//读取变量t的线程,若读取的值和设置的值不一致,说明变量t的数据被破坏了,即线程不安全

public static class ReadT implements Runnable{

public void run() {

//不断的读取NotAtomicity的t的值

while (true) {

long tmp = NotAtomicity.getT();

//比较是否是自己设值的其中一个

if (tmp != 100L && tmp != 200L && tmp != -300L && tmp != -400L) {

//程序若执行到这里,说明long类型变量

t,其数据已经被破坏了

System.out.println(tmp);

}

////将当前线程的执行时间片段让出去,以便由线程调度机制重新决定哪个线程可以执行

Thread.yield();

}

}

}

public static void main(String[] args) {

new Thread(new ChangeT(100L)).start();

new Thread(new ChangeT(200L)).start();

new Thread(new ChangeT(-300L)).start();

new Thread(new ChangeT(-400L)).start();

new Thread(new ReadT()).start();

}

}

我们创建了4个线程来对long类型的变量t进行赋值,赋值分别为100,200,-300,-400,有一个线程负责读取变量t,如果正常的话,读取到的t的值应该是我们赋值中的一个,但是在32的JVM中(ps: 64位的就别想了),事情会出乎预料。如果程序正常的话,我们控制台不会有任何的输出,可实际上,程序一运行,控制台就输出了下面的信息:

-4294967096

4294966896

-4294967096

-4294967096

4294966896

之所以会出现上面的情况,是因为在32位JVM中,64位的long数据的读和写都不是原子操作,即不具有原子性,并发的时候相互干扰了。

32位的JVM中,要想保证对long、double类型数据的操作的原子性,可以对访问该数据的方法进行同步,就像下面的:

【代码2】

public class Atomicity {

//静态变量t

public static long t = 0;

//静态变量t的get方法,同步方法

public synchronized static long getT() {

return t;

}

//静态变量t的set方法,同步方法

public synchronized static void setT(long t) {

Atomicity.t = t;

}

//改变变量t的线程

public static class ChangeT implements Runnable{

private long to;

public ChangeT(long to) {

this.to = to;

}

public void run() {

//不断的将long变量设值到 t中

while (true) {

Atomicity.setT(to);

//将当前线程的执行时间片段让出去,以便由线程调度机制重新决定哪个线程可以执行

Thread.yield();

}

}

}

//读取变量t的线程,若读取的值和设置的值不一致,说明变量t的数据被破坏了,即线程不安全

public static class ReadT implements Runnable{

public void run() {

//不断的读取NotAtomicity的t的值

while (true) {

long tmp = Atomicity.getT();

//比较是否是自己设值的其中一个

if (tmp != 100L && tmp != 200L && tmp != -300L && tmp != -400L) {

//程序若执行到这里,说明long类型变量

t,其数据已经被破坏了

System.out.println(tmp);

}

////将当前线程的执行时间片段让出去,以便由线程调度机制重新决定哪个线程可以执行

Thread.yield();

}

}

}

public static void main(String[] args) {

new Thread(new ChangeT(100L)).start();

new Thread(new ChangeT(200L)).start();

new Thread(new ChangeT(-300L)).start();

new Thread(new ChangeT(-400L)).start();

new Thread(new ReadT()).start();

}

}

这样做的话,可以保证对64位数据操作的原子性。

可见性

一个线程对共享变量做了修改之后,其他的线程立即能够看到(感知到)该变量这种修改(变化)。

Java内存模型是通过将在工作内存中的变量修改后的值同步到主内存,在读取变量前从主内存刷新最新值到工作内存中,这种依赖主内存的方式来实现可见性的。

无论是普通变量还是volatile变量都是如此,区别在于:volatile的特殊规则保证了volatile变量值修改后的新值立刻同步到主内存,每次使用volatile变量前立即从主内存中刷新,因此volatile保证了多线程之间的操作变量的可见性,而普通变量则不能保证这一点。

除了volatile关键字能实现可见性之外,还有synchronized,Lock,final也是可以的。使用synchronized关键字,在同步方法/同步块开始时(Monitor Enter),使用共享变量时会从主内存中刷新变量值到工作内存中(即从主内存中读取最新值到线程私有的工作内存中),在同步方法/同步块结束时(Monitor Exit),会将工作内存中的变量值同步到主内存中去(即将线程私有的工作内存中的值写入到主内存进行同步)。

使用Lock接口的最常用的实现ReentrantLock(重入锁)来实现可见性:当我们在方法的开始位置执行lock.lock()方法,这和synchronized开始位置(Monitor Enter)有相同的语义,即使用共享变量时会从主内存中刷新变量值到工作内存中(即从主内存中读取最新值到线程私有的工作内存中),在方法的最后finally块里执行lock.unlock()方法,和synchronized结束位置(Monitor Exit)有相同的语义,即会将工作内存中的变量值同步到主内存中去(即将线程私有的工作内存中的值写入到主内存进行同步)。

final关键字的可见性是指:被final修饰的变量,在构造函数数一旦初始化完成,并且在构造函数中并没有把“this”的引用传递出去(“this”引用逃逸是很危险的,其他的线程很可能通过该引用访问到只“初始化一半”的对象),那么其他线程就可以看到

final变量的值。

有序性

对于一个线程的代码而言,我们总是以为代码的执行是从前往后的,依次执行的。这么说不能说完全不对,在单线程程序里,确实会这样执行;但是在多线程并发时,程序的执行就有可能出现乱序。用一句话可以总结为:在本线程内观察,操作都是有序的;如果在一个线程中观察另外一个线程,所有的操作都是无序的。前半句是指“线程内表现为串行语义(WithIn Thread As-if-Serial Semantics)”,后半句是指“指令重排”现象和“工作内存和主内存同步延迟”现象。

Java提供了两个关键字volatile和synchronized来保证多线程之间操作的有序

性,volatile关键字本身通过加入内存屏障来禁止指令的重排序,而synchronized关键字通过一个变量在同一时间只允许有一个线程对其进行加锁的规则来实现,在单线程程序中,不会发生“指令重排”和“工作内存和主内存同步延迟”现象,只在多线程程序中出现。

happens-before原则

Java内存模型中定义的两项操作之间的次序关系,如果说操作A先行发生于操作B,操作A产生的影响能被操作B观察到,“影响”包含了修改了内存中共享变量的值、发送了消息、调用了方法等。

下面是Java内存模型下一些”天然的“happens-before关系,这些happens-before关系无须任何同步器协助就已经存在,可以在编码中直接使用。如果两个操作之间的关系不在此列,并且无法从下列规则推导出来的话,它们就没有顺序性保障,虚拟机可以对它们进行随意地重排序。

1.程序次序规则(Pragram Order Rule):在一个线程内,按照程序代码顺序,书写在前面的

操作先行发生于书写在后面的操作。准确地说应该是控制流顺序而不是程序代码顺序,因为要考虑分支、循环结构。

2.管程锁定规则(Monitor Lock Rule):一个unlock操作先行发生于后面对同一个锁的lock

操作。这里必须强调的是同一个锁,而”后面“是指时间上的先后顺序。

3.volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面

对这个变量的读取操作,这里的”后面“同样指时间上的先后顺序。

4.线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个

动作。

5.线程终于规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终

止检测,我们可以通过Thread.join()方法结束,Thread.isAlive()的返回值等作段检测到线程已经终止执行。

6.线程中断规则(Thread Interruption Rule):对线程interrupt()方法的调用先行发生于被中

断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测是否有中断发生。

7.对象终结规则(Finalizer Rule):一个对象初始化完成(构造方法执行完成)先行发生于它的

finalize()方法的开始。

8.传递性(Transitivity):如果操作A先行发生于操作B,操作B先行发生于操作C,那就可

以得出操作A先行发生于操作C的结论。

一个操作”时间上的先发生“不代表这个操作会是“先行发生",那如果一个操作"先行发生"是否就能推导出这个操作必定是"时间上的先发生"呢?也是不成立的,一个典型的例子就是指令重排序。所以时间上的先后顺序与happens-before原则之间基本没有什么关系,所以衡量并发安全问题一切必须以happens-before 原则为准。

java技术面试必问:JVM 内存模型讲解

java技术面试必问:JVM 内存模型讲解 今天我们就来聊一聊Java内存模型,面试中面试官会通过考察你对jvm的理解更深入得了解你的水平。在了解jvm内存模型前我们先回顾下,java程序的执行过程: java文件在通过java编译器生产.class 字节码文件,然后由jvm中的类加载器加载各个类中的字节码文件,加载完成后由jvm执行引擎执行,在整个加载过程中,jvm用一段空间来存储程序执行期间需要的数据和相关信息,这个空间就叫做jvm内存。 一、JVM 的重要性 首先你应该知道,运行一个 Java 应用程序,我们必须要先安装 JDK 或者 JRE 。这是因为 Java 应用在编译后会变成字节码,然后通过字节码运行在 JVM 中,而 JVM 是JRE 的核心组成部分。 二、优点 JVM 不仅承担了 Java 字节码的分析(JIT compiler)和执行(Runtime),同时也内置了自动内存分配管理机制。这个机制可以大大降低手动分配回收机制可能带来的内存泄露和内存溢出风险,使 Java 开发人员不需要关注每个对象的内存分配以及回收,从而更专注于业务本身。 三、缺点 这个机制在提升 Java 开发效率的同时,也容易使 Java 开发人员过度依赖于自动化,弱化对内存的管理能力,这样系统就很容易发生 JVM 的堆内存异常、垃圾回收(GC)的不合适以及 GC 次数过于频繁等问题,这些都将直接影响到应用服务的性能。 四、内存模型 JVM 内存模型共分为5个区:堆(Heap)、方法区(Method Area)、程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)。 其中,堆(Heap)、方法区(Method Area)为线程共享,程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)为线程隔离。 五、堆(Heap) 堆是 JVM 内存中最大的一块内存空间,该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中。 堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 区和 Survivor 区,最后 Survivor 由 From Survivor 和 To Survivor 组成。

java数据在内存中存储详解

博客分类: JAVA 1. 有这样一种说法,如今争锋于IT战场的两大势力,MS一族偏重于底层实现,Java 一族偏重于系统架构。说法根据无从考证,但从两大势力各自的社区力量和图书市场已有佳作不难看出,此说法不虚,但掌握Java的底层实现对Java程序员来说是至关重要的,本文介绍了Java中的数据在内存中的存储。 2内存中的堆(stack)与栈(heap) Java程序运行时有6个地方可以存储数据,它们分别是寄存器、栈、堆、静态存储、常量存储和非RAM存储,主要是堆与栈的存储。 【随机存储器:Random Access Memory】 栈与堆都是Java用来在RAM中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。另外,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。 【寄存器位于CPU中】 3Java中数据在内存中的存储 3.1基本数据类型的存储 Java的基本数据类型共有8种,即int,short,long,byte,float,double, boolean,char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a=3;long b=255L;的形式来定义的,称为自动变量。值得注意的是:自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a=3;这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。

java内存屏障与JVM并发详解

深入Java底层:内存屏障与JVM并发详解(1) 本文介绍了内存屏障对多线程程序的影响,同时将研究内存屏障与JVM并发机制的关系,如易变量(volatile)、同步(synchronized)和原子条件式(atomic conditional)。 AD:内存屏障,又称内存栅栏,是一组处理器指令,用于实现对内存操作的顺序限制。本文假定读者已经充分掌握了相关概念和Java内存模型,不讨论并发互斥、并行机制和原子性。内存屏障用来实现并发编程中称为可见性(visibility)的同样重要的作用。 关于JVM更多内容,请参阅:JVM详解 Java虚拟机原理与优化 内存屏障为何重要? 对主存的一次访问一般花费硬件的数百次时钟周期。处理器通过缓存(caching)能够从数量级上降低内存延迟的成本这些缓存为了性能重新排列待定内存操作的顺序。也就是说,程序的读写操作不一定会按照它要求处理器的顺序执行。当数据是不可变的,同时/或者数据限制在线程范围内,这些优化是无害的。 如果把这些优化与对称多处理(symmetric multi-processing)和共享可变状态(shared mutable state)结合,那么就是一场噩梦。当基于共享可变状态的内存操作被重新排序时,程序可能行为不定。一个线程写入的数据可能被其他线程可见,原因是数据写入的顺序不一致。适当的放置内存屏障通过强制处理器顺序执行待定的内存操作来避免这个问题。 内存屏障的协调作用 内存屏障不直接由JVM暴露,相反它们被JVM插入到指令序列中以维持语言层并发原语的语义。我们研究几个简单Java程序的源代码和汇编指令。首先快速看一下Dekker算法中的内存屏障。该算法利用volatile变量协调两个线程之间的共享资源访问。 请不要关注该算法的出色细节。哪些部分是相关的?每个线程通过发信号试图进入代码第一行的关键区域。如果线程在第三行意识到冲突(两个线程都要访问),通过turn变量的操作来解决。在任何时刻只有一个线程可以访问关键区域。 1. // code run by first thread // code run by second thread 2. 3. 1 intentFirst = true; intentSecond = true;

Java内存区域划分、内存分配原理

本文由我司收集整编,推荐下载,如有疑问,请与我司联系 Java 内存区域划分、内存分配原理 2014/11/16 2448 运行时数据区域 Java 虚拟机在执行Java 的过程中会把管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程 的启动而存在,而有的区域则依赖线程的启动和结束而创建和销毁。 Java 虚拟机包括下面几个运行时数据区域: 程序计数器 程序计数器是一块较小的区域,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的模型里,字节码指示器就是通过改变程序计数器的值 来指定下一条需要执行的指令。分支,循环等基础功能就是依赖程序计数器来完成的。 由于java 虚拟机的多线程是通过轮流切换并分配处理器执行时间来完成,一个处理器同一时间只会执行一条线程中的指令。为了线程恢复后能够恢复正确的 执行位置,每条线程都需要一个独立的程序计数器,以确保线程之间互不影响。因 此程序计数器是“线程私有”的内存。 如果虚拟机正在执行的是一个Java 方法,则计数器指定的是字节码指令对应的地址,如果正在执行的是一个本地方法,则计数器指定问空undefined。程序计数器区域是Java 虚拟机中唯一没有定义OutOfMemory 异常的区域。 Java 虚拟机栈 和程序计数器一样也是线程私有的,生命周期与线程相同。虚拟机栈描述的是Java 方法执行的内存模型:每个方法被执行的时候都会创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用的过程就对应 一个栈帧在虚拟机栈中从入栈到出栈的过程。

精选大厂java多线程面试题50题

Java多线程50题 1)什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。 2)线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。更多详细信息请点击这里。 3)如何在Java中实现线程? https://www.360docs.net/doc/e317207735.html,ng.Thread类的实例就是一个线程但是它需要调用https://www.360docs.net/doc/e317207735.html,ng.Runnable接口来执行,由于线程类本身就是调用的 Runnable接口所以你可以继承https://www.360docs.net/doc/e317207735.html,ng.Thread类或者直接调用Runnable接口来重写run()方法实现线程。 4)Thread类中的start()和run()方法有什么区别? 这个问题经常被问到,但还是能从此区分出面试者对Java线程模型的理解程度。start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你

调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。 5)Java中Runnable和Callable有什么不同? Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。它们的主要区别是Callable的call()方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。 6)Java内存模型是什么? Java内存模型规定和指引Java程序在不同的内存架构、CPU 和操作系统间有确定性地行为。它在多线程的情况下尤其重要。 Java内存模型对一个线程所做的变动能被其它线程可见提供了保证,它们之间是先行发生关系。 ●线程内的代码能够按先后顺序执行,这被称为程序次序 规则。 ●对于同一个锁,一个解锁操作一定要发生在时间上后发 生的另一个锁定操作之前,也叫做管程锁定规则。 ●前一个对Volatile的写操作在后一个volatile的读操作之 前,也叫volatile变量规则。 ●一个线程内的任何操作必需在这个线程的start()调用之 后,也叫作线程启动规则。 ●一个线程的所有操作都会在线程终止之前,线程终止规

字节跳动面试经验分享(非常详细)

字节跳动面试官一招差点KO我,一共面试了3轮(5年经验),艰难拿下2-2职级offer! 前言 我从大学毕业开始做Android开发,现在已经五年时间了,现在在山东老家济南做Android开发。这三年里面,也只是一心在做Android开发,其他语言接触的并不多,了解点JS之类的。现在感觉Android开始不像以前那样好做了,也可能是现在年纪慢慢大了,要考虑的事情变多了的缘故吧。 不知道以后应该何去何从,总是感觉做Android或者说做程序员一直处在一种不稳定之中,在一些中小公司里面,可能工作一年两年就因为各种各样的原因而离职。马上就要结婚了,该买房了。济南的房价一直在涨,而自己的收入还是这么不温不火的,加上这不稳定的工作,让人对于前途实在是乐观不起来。 再加上今年的大环境非常差,互联网企业裁员的现象比往年更严重了,可今年刚好是我的第一个“五年计划”截止的时间点,说什么也不能够耽搁了,所以早早准备的跳槽也在疫情好转之后开始进行了。但是,不得不说,这次字节的面试真的太难为我了,可以说是和面试官大战了3个回合,不过好在最后给了offer。 我个人情况是5年Android开发经验,字节跳动定级2-2(年薪是50-100w左右含加班费和股票折现,不含车餐房补)的样子,我是拿到了年薪60w+,13薪。下面是我的面试经历,与学习经验分享,希望能带来一些不一样的启发和帮助。 我与字节跳动面试官“大战”3回合,胜! 我的学习经验 1—4年大学 ?Java

无论什么级别的Android从业者,Java作为Android开发基础语言。不管是工作还是面试中,Java都是必考题。如果不懂Java的话,薪酬会非常吃亏(美团尤为重视Java基础) 详细介绍了Java泛型、注解、并发编程、数据传输与序列化、高效IO、容器集合、反射与类加载以及JVM重点知识线程、内存模型、JVM运行时内存、垃圾回收与算法、Java中四种引用类型、GC 分代收集算法 VS 分区收集算法、GC 垃圾收集器、JAVA IO/NIO 、JVM 类加载机制的各大知识点。 ?筑基必备 Android架构师筑基包括哪些内容呢: 1.深入 Java 泛型. 2.解深入浅出 3.并发编程 4.数据传输与序列化 5.Java 虚拟机原理 6.反射与类加载 7.高效 IO 8.Kotlin项目实战 大学1-4年架构师筑基必备 ?学习笔记整理 架构师筑基必备目录 架构师筑基必备第一章

java初级面试题

JAVA相关基础知识 1.Finally,final,finalize Finally:释放资源(内存之外的,打开的文件、连接、屏幕上的图形,,) ①总会执行 ②非后台线程结束,后台线程被强关,不会执行finally ③当try和catch中有return时,finally在return之后执行,但是返回值不会改变(finally中不会改变已保存的返回结果) ④finally中最好不要包含return,否则程序会从finally中退出,返回值不是try或catch中保存的返回值。 final: 基本数据类型:不可更改 类:不可继承 对象:引用不可变,对象内容可变 finalze:回收前调用,不适合用来清理或释放资源。对象免死最后机会!保证会被调用,但不保证会执行完(在低优先级线程中执行) 2. 数据在各个网络层之间是怎么传输的? 数据在各层之间的单位都是不一样的, 在物理层数据的单位称为比特(bit);在数据链路层,数据的单位称为(frame);在网络层,数据的单位称为数据包(packet);传输层,数据的单位称为数据段(segment)。 3. Hashtable、HashMap Hashtable 与HashMap类似,但是主要有6点不同。 1.HashTable的方法是同步的,HashMap未经同步,如Vector和ArrayList一样。 2.HashTable不允许null,key和value都不可以,HashMap允许null值,key和value 都可以。HashMap允许key值只能由一个null

3.HashTable有一个contains(Object value)功能和containsValue(Object value)功能一样。 4.遍历的时候,HashTable使用Enumeration,HashMap使用Iterator。 5.HashTable中hash数组默认大小是11,增加的方式是old*2+1。HashMap中hash 数组的默认大小是16,而且一定是2的指数。 6.哈希值的使用不同,HashTable直接使用对象的hashCode。 Hashtable继承自Dictionary类,实现了Map接口。而HashMap是继承自AbstractMap,实现了Map接口。 4. GET,POST区别? 答:基础知识:Http的请求格式如下。 主要包含三个信息:1、请求的类型(GET或POST),2、要访问的资源(如\res\img\a.jif),3、Http版本(http/1.1)

用来说明服务器要使用的附加信息 这是Http的规定,必须空一行 [] 请求的内容数据 区别: 1、Get是从服务器端获取数据,Post则是向服务器端发送数据。 2、在客户端,Get方式通过URL提交数据,在URL地址栏可以看到请求消息,该消息被编码过;Post数据则是放在Html header内提交。

JAVA经典面试题:Java内存模型

JAVA经典面试题:Java内存模型 因为Java内存模型不仅是java重点要学习的技术知识,还是面试的时候经典面试题,希望引起同学们的重视,今天千锋小编就来分享一下java内存模型的相关技术知识。 不同的渠道,内存模型是不一样的,但是jvm的内存模型标准是一致的。其实java的多线程并发问题都会反映在java的内存模型上,所谓线程安全无非是要操控多个线程对某个资源的有序拜访或修改。总结java的内存模型,要解决两个首要的问题:可见性和有序性。 可见性:多个线程之间是不能相互传递数据通信的,它们之间的交流只能经过同享变量来进行。Java内存模型(JMM)规定了jvm有主内存,主内存是多个线程同享的。当new一个目标的时分,也是被分配在主内存中,每个线程都有自己的作业内存,作业内存存储了主存的某些目标的副本,当然线程的作业内存大小是有限制的。当线程操作某个目标时,履行次序如下: (1) 从主存仿制变量到当前作业内存(read and load) (2) 履行代码,改动同享变量值(use and assign) (3) 用作业内存数据改写主存相关内容(store and write)

当一个同享变量在多个线程的作业内存中都有副本时,如果一个线程修改了这个同享变量,那么其他线程应该可以看到这个被修改后的值,这就是多线程的可见性问题。 有序性:线程在引证变量时不能直接从主内存中引证,如果线程作业内存中没有该变量,则会从主内存中复制一个副本到作业内存中,完成后线程会引证该副本。当同一线程再度引证该字段时,有可能从头从主存中获取变量副本(read-load-use),也有可能直接引证本来的副本(use),也就是说read,load,use次序可以由JVM完成体系决议。 线程不能直接为主存中字段赋值,它会将值指定给作业内存中的变量副本(assign),完成后这个变量副本会同步到主存储区(store- write),至于何时同步往昔,依据JVM完成体系决议。有该字段,则会从主内存中将该字段赋值到作业内存中,这个进程为read-load,完成后线程会引证该变量副本。 知识就财富,这句话再IT行业显示的尤其现实残酷,懂就是懂,不懂就是不懂,所以各位同学,你的努力与否与你财富直接挂钩。一起加油吧!更多java 技术经典面试题欢迎关注千锋小编。

杭研Java秋招面经——【网易 笔试面试精品资源】

一面 Java内存模型讲一下 GC算法,知道的都讲一下 HashMap,get,put实现 JsonWebToken具体实现流程(简历) Spring AOP如何实现,写一个 AOP功能的主要流程 数据库引擎用过哪些,它们的区别 设计大流量访问系统,要做节流控制(类似秒杀) Linux命令用过哪些 频繁 gc排查处理 内存过大排查的处理,用 jmap,jstack怎么做,不用又怎么做 MySQL主从复制场景问题(bin_log) 项目中技术上最大的成长,项目中的问题解决方案讲一下 二面 手撕算法:比如 123+234=357,对应两个链表 3->2->1,4->3->2(输

入),输出结果链表(7->5->3),写一个函数实现,输入为两个链表,输出为一个结果链表。(考虑极端情况和进位情况,花了很久写出来还是不完善) 输入网址到展现发生了什么,越详细越好(我考虑了 DNS轮询,负载均衡和 CDN以及状态码,然后就全讲) 负载均衡是怎么做的,CDN具体是怎么实现的 TCP三次握手四次挥手 cookie和 session的区别,多台服务器的情况呢 四次挥手时最后两者的状态,Client的 TIME_WAIT避免什么问题,没有它会怎么样 SSM和 Spring Boot的区别 MyBatis和 Hibernate区别

TCP流量控制和拥塞控制,具体在场景中是怎么起作用的 Java线程和 OS中的线程的关系,与内存对应关系,一个 JVM线程数的上限受哪些因素限制 HR 挨个讲讲项目 项目哪个对技术成长大,哪个对个人成长大 面了哪些公司,走到了什么流程 为什么选择网易 之后的学习规划 全程自己讲了很多 总监面 protocolbuf主要优势(性能、安全性、跨语言) Java本身序列化存在的问题(不知道) Java内存模型 网络 IO编程中的内存使用了 JMM哪部分

java内存模型

12.Java内存模型 (原本准备把内存模型单独放到某一篇文章的某个章节里面讲解,后来查阅了国外很多文档才发现其实JVM内存模型的内容还蛮多的,所以直接作为一个章节的基础知识来讲解,可能该章节概念的东西比较多。一个开发Java的开发者,一旦了解了JVM内存模型就能够更加深入地了解该语言的语言特性,可能这个章节更多的是概念,没有太多代码实例,所以希望读者谅解,有什么笔误来Email告知:silentbalanceyh@https://www.360docs.net/doc/e317207735.html,,本文尽量涵盖所有Java语言可以碰到的和内存相关的内容,同样也会提到一些和内存相关的计算机语言的一些知识,为草案。因为平时开发的时候没有特殊情况不会进行内存管理,所以有可能有笔误的地方比较多,我用的是Windows平台,所以本文涉及到的与操作系统相关的只是仅仅局限于Windows平台。不仅仅如此,这一个章节牵涉到的多线程和另外一些内容并没有讲到,这里主要是结合JVM内部特性把本章节作为核心的概念性章节来讲解,这样方便初学者深入以及彻底理解Java 语言) 本文章节: 1.JMM简介 2.堆和栈 3.本机内存 4.防止内存泄漏 1.JMM简介 i.内存模型概述 Java平台自动集成了线程以及多处理器技术,这种集成程度比Java以前诞生的计算机语言要厉害很多,该语言针对多种异构平台的平台独立性而使用的多线程技术支持也是具有开拓性的一面,有时候在开发Java同步和线程安全要求很严格的程序时,往往容易混淆的一个概念就是内存模型。究竟什么是内存模型?内存模型描述了程序中各个变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节,对象最终是存储在内存里面的,这点没有错,但是编译器、运行库、处理器或者系统缓存可以有特权在变量指定内存位置存储或者取出变量的值。【JMM】(Java Memory Model的缩写)允许编译器和缓存以数据在处理器特定的缓存(或寄存器)和主存之间移动的次序拥有重要的特权,除非程序员使用了final或synchronized明确请求了某些可见性的保证。 1)JSR133:

jvm内存结构

结构概览 JVM内存区域也称为Java运行时数据区域。其中包括:程序计数器、栈、堆、方法区等。 内存结构主要分为三大部分:堆内存,方法区和栈。 堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间。 方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆);栈又分为java虚拟机栈和本地方法栈主要用于方法的执行。 JVM和系统调用之间的关系:

Java堆(Heap) Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。 根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。 简要归纳:新的对象分配是首先放在年轻代 (Young Generation) 的Eden区,Survivor区作为 Eden区和Old区的缓冲,在Survivor区的对象经历若干次收集仍然存活的,就会被转移到老年代Old中。 方法区(Method Area) 是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据(存放的是Class)。它有一个别名,叫非堆。

Java新手必学的21个技术点

Java新手必学的21个技术点 以下为大家盘点作为Java新手必学的21个技术点,希望能够对想要学习编程,学习JAVA的人有些帮助! JNI Java Native Interface,可以允许Java中调用本地接口方法,一般用于C/C++代码的调用。需要注意的是在java中加载so/dll文件的路径问题,本身调用接口并不复杂,但是经常在是否加载了所需的本地接口库中花费较多时间 RMI RemoteMethodInvocation ,java语言特有的远程调用接口,使用还是比较简单方便。不过需要跨语言的情况下,就需要使用 webservice 等其他方式来支持。一般来说,程序都不需要使用RMI,不过可以在特定的情况下使用,我就在一个项目中,使用RMI来进行程序远程启动停止的控制。 标注 也是jdk5 之后引入的。Spring是个优秀的框架,最开始就以xml作为标准的配置文件。不过到了Spring3 之后,尤其是 spring-boot 兴起之后,越来越推崇使用标注来简化xml配置文件了,对于开发者来说,可以节省不少xml配置的时间。但是劣势是在于标注散落在各个类中,不像xml,可以对所有配置有个全局性的理解和管理,所以还没有办法说完全就取代所有的xml。对于一般开发者,会使用标注即可,一些公共组建的开发者可能会需要了解标注的定义和实现,可以在具体需要的时候再细看。 泛型

这是JDK5开始引入的新概念,其实是个语法糖,在编写java代码时会有些许便利,一般的应用或者是业务的开发,只需要简单使用,不一定会用到定义泛型这样的操作,但是开发一些基础公共组件会使用到,可以在需要的时候再细看这个部分,一般情况下只要会简单使用即可。 Maven的使用 Maven 也不是Java里面的内容,但是maven是革命性的,给java开发带来了巨大的便利。从依赖的引入和管理,开发流程的更新和发布产出,乃至版本的更新,使用maven可以大大简化开发过程中的复杂度,从而节省大量时间。可以说,maven已经成为java开发者的标配了。所以我把maven也作为一个java开发者对于基础必备的知识点。以后会再放上一些我的一些对于maven使用的经验和技巧等,这里就不再细说了 XML解析/ JSON解析 其实这两块内容都不是J2SE里面的内容,但是在日常开发中,和其他程序交互,和配置文件交互,越来越离不开这两种格式的解析。 不过对于一个开发者来说,能够了解一些XML/JSON具体解析的原理和方法,有助于你在各个具体的场景中更好的选择合适你的方式来使得你的程序更有效率和更加健壮。 XML:需要了解 DOM解析和 SAX解析的基本原理和各自的适用场景JSON:需要了解一些常用JSON框架的用法, 如 Jackson, FastJson, Gson 等。 时间日期处理

java面试2019

J2SE基础: 1. 九种基本数据类型的大小,以及他们的封装类。 2. Switch能否用string做参数? 3. equals与==的区别。 4. Object有哪些公用方法? 5. Java的四种引用,强弱软虚,用到的场景。 6. Hashcode的作用。 7. ArrayList、LinkedList、Vector的区别。 8. String、StringBuffer与StringBuilder的区别。 9. Map、Set、List、Queue、Stack的特点与用法。 10. HashMap和HashTable的区别。 11. HashMap和ConcurrentHashMap的区别,HashMap的底层源码。 12. TreeMap、HashMap、LindedHashMap的区别。 13. Collection包结构,与Collections的区别。 14. try catch finally,try里有return,finally还执行么? 15. Excption与Error包结构。OOM你遇到过哪些情况,SOF你遇到过哪些情况。 16. Java面向对象的三个特征与含义。 17. Override和Overload的含义去区别。 18. Interface与abstract类的区别。 19. Static class 与non static class的区别。 20. java多态的实现原理。 21. 实现多线程的两种方法:Thread与Runable。 22. 线程同步的方法:sychronized、lock、reentrantLock等。 23. 锁的等级:方法锁、对象锁、类锁。

适用于云计算的数据库开发和使用案例

适用于云计算的数据库 开发及案例
Copyright ? Versant Corp. All rights reserved.
0001
By Tiger Lau,CTO of Versant China

数据库发展简史
模型
层次化,结构化 网络化 关系型 对象型
优点
性能 性能 灵活性, 支持查询 性能,灵活性
缺点
灵活性, 对查询的支持 灵活性, 对查询的支持 性能 随机性较强, 支持既成的查询
数据
简单 简单 简单 复杂
这两点是现有很多系统的核心问题所在

什么是Versant云数据库
Versant数据库是大规模的分布式数据库 Versant数据库是专门为复杂数据提供服务的数据库:
设计目标是优化对象的存储与操作。 有能力管理任何类型的复杂模型。
简单类型: 整型, 字符串 数据类型与程序语言定义完全一致, 非自建数据结构 多值类型(Multi-valued): 动态类型数组 可以有效支持复杂程序数据结构,无需拆分
有能力管理对象间的任何关系。
对象间引用 (链接) 集合 (唯一性), 列表 (排序), 图 (关联性查找) 一次性存储,透明装载,能极大提高系统效率
Versant数据库能够实现数据与程序语言的无缝集成。

什么是复杂数据?
应用自身的数据结构以及数据非常复杂。
面向图形/GIS的复杂应用系统 基于复杂对象导航访问模式的应用系统
受到面向对象模型的影响而形成的复杂数据。
继承 集合 关联
由于无法简单的映射到关系模型而 形成的复杂数据。
大量的映射代码 大量的联合(JOIN)操作 性能不佳

JAVA 内存管理总结

JAVA 内存管理总结 1. java是如何管理内存的 Java的内存管理就是对象的分配和释放问题。(两部分) 分配:内存的分配是由程序完成的,程序员需要通过关键字new为每个对象申请内存空间(基本类型除外),所有的对象都在堆(Heap)中分配空间。 释放:对象的释放是由垃圾回收机制决定和执行的,这样做确实简化了程序员的工作。但同时,它也加重了JVM的工作。因为,GC为了能够正确释放对象,GC必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。 2. 什么叫java的内存泄露 在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连(也就是说仍存在该内存对象的引用);其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。 3. JVM的内存区域组成 java把内存分两种:一种是栈内存,另一种是堆内存1。在函数中定义的基本类型变量和对象的引用变量都在函数的栈内存中分配;2。堆内存用来存放由new创建的对象和数组以及对象的实例变量在函数(代码块)中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量所分配的内存空间;在堆中分配的内存由java虚拟机的自动垃圾回收器来管理 堆和栈的优缺点 堆的优势是可以动态分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的。 缺点就是要在运行时动态分配内存,存取速度较慢;栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。 另外,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。 4. Java中数据在内存中是如何存储的 a) 基本数据类型

Java并发编程:JMM(Java内存模型)和volatile

Java并发编程:JMM(Java内存模型)和volatile 1. 并发编程的3个概念 并发编程时,要想并发程序正确地执行,必须要保证原子性、可见性和有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。 1.1. 原子性 原子性:即一个或多个操作要么全部执行并且执行过程中不会被打断,要么都不执行。 一个经典的例子就是银行转账:从账户A向账户B转账1000元,此时包含两个操作:账户A减去1000元,账户B加上1000元。这两个操作必须具备原子性才能保证转账安全。假如账户A减去1000元之后,操作被打断了,账户B却没有收到转过来的1000元,此时就出问题了。 1.2. 可见性 可见性:即多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的最新值。 例如下段代码,线程1修改i的值,线程2却没有立即看到线程1修改的i的最新值: 假如执行线程1的是CPU1,执行线程2的是CPU2。当线程1执行i=10 时,会将CPU1的高速缓存中i的值赋值为10,却没有立即写入主内存中。此时线程2执行j=i,会

先从主内存中读取i的值并加载到CPU2的高速缓存中,此时主内存中的i=0,那么就会使得j最终赋值为0,而不是10。 1.3. 有序性 有序性:即程序执行的顺序按代码的先后顺序执行。 例如下面这段代码: 在代码顺序上i=1 在flag=true 前面,而JVM 在真正执行代码的时候不一定能保证i=1 在flag=true 前面执行,这里就发生了指令重排序。 指令重排序 一般是为了提升程序运行效率,编译器或处理器通常会做指令重排序: ?编译器重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序 ?处理器重排序:如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。CPU 在指令重排序时会考虑指令之间的数据依赖性,如果指令2必须依赖用到指令1的结果,那么CPU会保证指令1在指令2之前执行。 指令重排序不保证程序中各个语句的执行顺序和代码中的一致,但会保证程序最终执行结果和代码顺序执行的结果是一致的。比如上例中的代码,i=1 和flag=true 两个语句先后执行对最终的程序结果没有影响,就有可能CPU 先执行flag=true,后执行i=1。2. java 内存模型 由于volatile 关键字是与java 内存模型相关的,因此了解volatile 前,需要先了解下java 内存模型相关概念

JAVA虚拟机规范

运行时数据区域 Java虚拟机所管理的内存将会包括以下几个运行时数据区域: 程序计数器 Program Counter Register是一块较小的内存空间,作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机规范里,字节码解释器工作时就是通过改变计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理和线程恢复等基础功能都依赖程序计数器完成。 由于Java虚拟机的多线程时通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令。因为,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程之间的计数器不影响,独立存储,因此这类区域为“线程私有”的内存。 如果线程正在执行一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址:如果执行的是Native方法,计数器值为空Undefined。此区域是虚拟机规范中没有规定任何OutOfMemoryError情况的区域。 Java虚拟机栈 Java虚拟机栈也是线程私有的,生命周期与线程相同。虚拟机栈描述的是Java方法执

行的内存模型:每个方法被执行时会创建一个栈帧Stack Frame用于存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法被调用到执行完成,对应一个栈帧在栈中从入栈到出栈的过程。 局部变量表存放了编译期可知的8种基本数据类型、对象引用和returnAddress类型(指向一条字节码指令的地址)。其中64位长度的long和double类型的数据占用2个局部变量空间(Slot),其余数据类型占用1个。局部变量表所需的内存空间在编译期间完成分配,在方法运行期间不会改变局部变量表的大小。 在虚拟机规范中,这个区域规定了两种异常:如果线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常;如果栈可以动态扩展,当扩展无法申请到足够的内存时抛出OutOfMemoryError异常。 本地方法栈 Native Method Stack与虚拟机栈作用类似,不过是虚拟机栈为虚拟机执行的Java方法(即字节码)服务,而本地方法栈为虚拟机使用的Native方法服务。与虚拟机栈一样,本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。 Java堆 Java虚拟机规范描述是:所有对象实例以及数组都在堆上分配。堆是所有线程共享的,但从内存分配角度看,线程共享的堆可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer TLAB)。不过,无论如何划分,都与存放内容无关,无论哪个区域,存储的都仍然是对象实例。 方法区 Method Area是线程共享的,用于存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 Java虚拟机规范对方法区限制比较宽松,除了和堆一样不需要连续的内存和可用选择固定大小或者可扩展外,还可以选择不实现垃圾收集。一般来说,垃圾收集行为在这个区域比较少见,但并非数据进入了方法区就永久存在了。这个区域的内存回收目标主要针对常量池的回收和类型的卸载。该区域无法分配内存时,抛出OutOfMemoryError异常。 运行时常量池Runtime Constant Pool是方法区一部分,Class文件除了有类的版本、字段、方法和接口等描述信息外,还有常量池Constant Pool Table,用于存放编译期生成的字面量和符号引用。 Java语言不要求常量一定只能在编译期生成,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,比如String类的intern()方法。 对象访问 Object obj=new Object()

深入理解 Java String#intern() 内存模型

深入理解 Java String#intern() 内存模型

深入理解 Java String#intern() 内存模型 字符串常量池是一个固定大小的HashMap,桶的数量默认是1009, 从Java7u40开始,该默认值增大到60013。在Java6当中,字符串常量池是放在Perm空间的,从Java7开始,字符串常量池被移到Heap空间。下面,我们通过测试程序来窥探字符串常量池在Java6,Java7两个不同版本底下的内存分配情况。 测试程序 public class StringPoolTest { public void testStringPoolWithLongString(){ long i=0; while(true){ String longString = "This is a very long stri ng, very very long string to test the gc behavior of the string constant pool"+i; longString.intern(); i++; } } public static void main(String[] args){ StringPoolTest stringPoolTest = new StringPoolTes t(); stringPoolTest.testStringPoolWithLongString(); } } 测试程序很简单,一个死循环,循环里面通过递增变量i制造唯一的字符串,然后用main函数启动程序。 Java 6 我们使用版本Jdk1.6.0_29来跑该程序,打开Java VisualVM监控,可以看到,Perm区不断发生GC,由此的出结论,虽然字符串常量池放在Perm空间,但当Perm空间接近满的时候,JVM会将字符串常量池中的无用字符串回收掉。

15-JVM面试题(87题)

JVM面试题 1、java中会存在内存泄漏吗,请简单描述。 会。自己实现堆载的数据结构时有可能会出现内存泄露,可参看e?ective java. 2、64 位JVM 中,int 的长度是多数? Java 中,int 类型变量的长度是一个固定值,与平台无关,都是32 位。意思就是说,在32 位和64 位的Java 虚拟机中,int 类型的长度是相同的。 3、Serial 与Parallel GC 之间的不同之处? Serial 与Parallel 在GC 执行的时候都会引起stop-the-world。它们之间主要不同serial 收集器是默认的复制收集器,执行GC 的时候只有一个线程,而parallel 收集器使用多个GC 线程来执行。4、32 位和64 位的JVM,int 类型变量的长度是多数? 32 位和64 位的JVM 中,int 类型变量的长度是相同的,都是32 位或者4个字节。 5、Java 中WeakReference 与SoftReference 的区别? 虽然WeakReference 与SoftReference 都有利于提高GC 和内存的效率,但是WeakReference ,一旦失去最后一个强引用,就会被GC 回收,而软引用虽然不能阻止被回收,但是可以延迟到JVM 内存不足的时候。

6、JVM 选项-XX:+UseCompressedOops 有什么作用?为什么要使用 当你将你的应用从32 位的JVM 迁移到64 位的JVM 时,由于对象的指针从32 位增加到了64 位,因此堆内存会突然增加,差不多要翻倍。这也会对CPU缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到64 位的JVM主要动机在于可以指定最大堆大小,通过压缩OOP 可以节省一定的内存。通过-XX:+UseCompressedOops 选项,JVM 会使用32 位的OOP,而不是64 位的OOP。 7、怎样通过Java 程序来判断JVM 是32 位还是64位? 你可以检查某些系统属性如sun.arch.data.model 或os.arch 来获取该信息。 8、32 位JVM 和64 位JVM 的最大堆内存分别是多数? 理论上说上32 位的JVM 堆内存可以到达2^32,即4GB,但实际上会比这个小很多。不同操作系统之间不同,如Windows 系统大约1.5 GB,Solaris 大约3GB。64 位JVM 允许指定最大的堆内存,理论上可以达到2^64,这是一个非常大的数字,实际上你可以指定堆内存大小到100GB。甚至有的JVM,如Azul,堆内存到1000G 都是可能的。 9、JRE、JDK、JVM 及JIT 之间有什么不同? JRE 代表Java 运行时(Java run-time),是运行Java 引用所必须的。JDK 代表Java 开发工具(Java development kit),是Java 程序的开发工具,如Java编译器,它也包含JRE。JVM 代表Java 虚拟机(Java virtual machine),它的责任是运行Java 应用。JIT 代表即时编译(Just In Time compilation),当代码执行的次数超过一定的阈值时,会将Java 字节码转换为本地代码,如,主要的热点代码会被准换为本地代码,这样有利大幅度提高Java 应用的性能。 10、解释Java 堆空间及GC? 当通过Java 命令启动Java 进程的时候,会为它分配内存。内存的一部分用于创建堆空间,当程序中创建对象的时候,就从对空间中分配内存。GC 是JVM 内部的一个进程,回收无效对象的内存用于将来的分配。 11、JVM 内存区域

相关主题
相关文档
最新文档