深入理解Java内存模型:http://www.cnblogs.com/skywang12345/p/3447546.html

http://www.infoq.com/cn/articles/java-memory-model-1

原理

类加载的五个过程:加载、验证、准备、解析、初始化。

双亲委派模型:Bootstrap ClassLoader、Extension ClassLoader、ApplicationClassLoader。

分派:静态分派与动态分派。

1. Java的内存模型以及GC算法

1. 并发

定义:即,并发(同时)发生。在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。

并发需要处理两个关键问题:线程之间如何通信线程之间如何同步。

(01) 通信 —— 是指线程之间如何交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。

(02) 同步—— 是指程序用于控制不同线程之间操作发生相对顺序的机制。在Java中,可以通过volatile,synchronized, 锁等方式实现同步。

2.主内存和本地内存

主内存     —— 即main memory。在java中,实例域、静态域和数组元素是线程之间共享的数据,它们存储在主内存中。

本地内存 —— 即local memory。 局部变量,方法定义参数 和 异常处理器参数是不会在线程之间共享的,它们存储在线程的本地内存中。

3.重排序

定义:重排序是指“编译器和处理器”为了提高性能,而在程序执行时会对程序进行的重排序。

说明:重排序分为——“编译器”和“处理器”两个方面,而“处理器”重排序又包括“指令级重排序”和“内存的重排序”。

关于重排序,我们需要理解它的思想:为了提高程序的并发度,从而提高性能!但是对于多线程程序,重排序可能会导致程序执行的结果

不是我们需要的结果!因此,就需要我们通过“volatile,synchronize,锁等方式”作出正确的实现同步。

4.内存屏障

定义:包括LoadLoad, LoadStore, StoreLoad, StoreStore共种内存屏障。内存屏障是与相应的内存重排序相对应的。

屏障类型

指令示例

说明

LoadLoad Barriers

Load1; LoadLoad; Load2

确保Load1数据的装载,之前于Load2及所有后续装载指令的装载。

StoreStore Barriers

Store1; StoreStore; Store2

确保Store1数据对其他处理器可见(刷新到内存),之前于Store2及所有后续存储指令的存储。

LoadStore Barriers

Load1; LoadStore; Store2

确保Load1数据装载,之前于Store2及所有后续的存储指令刷新到内存。

StoreLoad Barriers

Store1; StoreLoad; Load2

确保Store1数据对其他处理器变得可见(指刷新到内存),之前于Load2及所有后续装载指令的装载。StoreLoad Barriers会使该屏障之前的所有内存访问指令(存储和装载指令)完成之后,才执行该屏障之后的内存访问指令。

作用:通过内存屏障可以禁止特定类型处理器的重排序,从而让程序按我们预想的流程去执行。

5. happens-before

定义:JDK5(JSR-133)提供的概念,用于描述多线程操作之间的内存可见性。如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。

作用:描述多线程操作之间的内存可见性。

[程序顺序规则]:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。

[监视器锁规则]:对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。

[volatile变量规则]:对一个volatile域的写,happens- before 于任意后续对这个volatile域的读。

[传递性]:如果A happens- before B,且B happens- before C,那么A happens- before C。

6. 数据依赖性

定义:如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。

作用:编译器和处理器不会对“存在数据依赖关系的两个操作”执行重排序。

7.as-if-serial

定义:不管怎么重排序,程序的执行结果不能被改变。

8. 顺序一致性内存模型

定义:它是理想化的内存模型。有以下规则:

(01) 一个线程中的所有操作必须按照程序的顺序来执行。

(02) 所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见。

9. JMM

定义:Java Memory Mode,即Java内存模型。它是Java线程之间通信的控制机制。

说明:JMM对Java程序作出保证——如果程序是正确同步的,程序的执行将具有顺序一致性。即,程序的执行结果与该程序在顺序一致性内存模型中的执行结果相同。

10. 可见性

可见性一般用于指不同线程之间的数据是否可见。

在java中, 实例域、静态域和数组元素这些数据是线程之间共享的数据,它们存储在主内存中;主内存中的所有数据对该内存中的线程都是可见的。而局部变量,方法定义参数 和 异常处理器参数这些数据是不会在线程之间共享的,它们存储在线程的本地内存中;它们对其它线程是不可见的。

此外,对于主内存中的数据,在本地内存中会对应的创建该数据的副本(相当于缓冲);这些副本对于其它线程也是不可见的。

11. 原子性

是指一个操作是按原子的方式执行的。要么该操作不被执行;要么以原子方式执行,即执行过程中不会被其它线程中断。

同步机制:

1.volatile

1.1 作用

如果一个变量是volatile类型,则对该变量的读写就将具有原子性。如果是多个volatile操作或类似于volatile++这种复合操作,这些操作整体上不具有原子性。volatile变量自身具有下列特性:

      [可见性]:对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。

      [原子性]:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。

1.2 volatile的内存语义

volatile:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。

volatile:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

1.3 JMM中的实现方式

JMM针对编译器制定的volatile重排序规则表:

是否能重排序

第二个操作

第一个操作

普通读/写

volatile读

volatile写

普通读/写

NO

volatile读

NO

NO

NO

volatile写

NO

NO

1.4 volatile和 synchronize对比

在功能上,监视器锁比volatile更强大;在可伸缩性和执行性能上,volatile更有优势。

volatile仅仅保证对单个volatile变量的读/写具有原子性;而synchronize锁的互斥执行的特性可以确保对整个临界区代码的执行具有原子性。

2.锁

2.1 作用

锁是java并发编程中最重要的同步机制。

2.2 锁的内存语义

(01) 线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息。

(02) 线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的)消息。

(03) 线程A释放锁,随后线程B获取这个锁,这个过程实质上是线程A通过主内存向线程B发送消息。

2.3 JMM如何实现锁

公平锁

公平锁是通过“volatile”实现同步的。公平锁在释放锁的最后写volatile变量state;在获取锁时首先读这个volatile变量。根据volatile的happens-before规则,释放锁的线程在写volatile变量之前可见的共享变量,在获取锁的线程读取同一个volatile变量后将立即变的对获取锁的线程可见。

非公平锁

通过CAS实现的,CAS就是compare and swap。CAS实际上调用的JNI函数,也就是CAS依赖于本地实现。以Intel来说,对于CAS的JNI实现函数,它保证:(01)禁止该CAS之前和之后的读和写指令重排序。(02)把写缓冲区中的所有数据刷新到内存中。

3.final

3.1 特性

对于基本类型的final域,编译器和处理器要遵守两个重排序规则:

(01) final写:“构造函数内对一个final域的写入”,与“随后把这个被构造对象的引用赋值给一个引用变量”,这两个操作之间不能重排序。

(02) final读:“初次读一个包含final域的对象的引用”,与“随后初次读对象的final域”,这两个操作之间不能重排序。

对于引用类型的final域,除上面两条之外,还有一条规则:

(03) final写:在“构造函数内对一个final引用的对象的成员域的写入”,与“随后在构造函数外把这个被构造对象的引用赋值给一个引用变量”,这两个操作之间不能重排序。

注意:

写final域的重排序规则可以确保:在引用变量为任意线程可见之前,该引用变量指向的对象的final域已经在构造函数中被正确初始化过了。其实要得到这个效果,还需要一个保证:在构造函数内部,不能让这个被构造对象的引用为其他线程可见,也就是对象引用不能在构造函数中“逸出”。

3.2 JMM如何实现final

通过“内存屏障”实现。

在final域的写之后,构造函数return之前,插入一个StoreStore障屏。在读final域的操作前面插入一个LoadLoad屏障。

2. jvm性能调优都做了什么

3. 介绍JVM中7个区域,然后把每个区域可能造成内存的溢出的情况说明

内存溢出和合理分配:http://blog.csdn.net/ye1992/article/details/9344807

http://flychao88.iteye.com/blog/2226205

http://www.itzhai.com/jvm-note-automatic-memory-management-mechanism.html#read-more

器池堆,栈栈区区

三个私有区:指令计数器,线程池,本地线程池

四个共享区:方法区,常量池,直接内存区,本地线程栈

堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的

在JVM中堆之外的内存称为非堆内存(Non-heap memory)

1.4.1、Java堆溢出

参数- XX:+ HeapDumpOnOutOfMemoryError

先通过内存映像分析工具(如 Eclipse Memory Analyzer) 对 Dump 出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏( Memory Leak) 还是内存溢出( Memory Overflow)。

如果是内存泄露,可进一步通过工具查看泄露对象到 GC Roots 的引用链。

如果不存在泄露,换句话说,就是内存中的对象确实都还必须存活着,那就应当检查虚拟机的堆参数(- Xmx 与- Xms)

1.4.2、虚拟机栈和本地方法栈溢出

在 HotSpot 虚拟机中并不区分虚拟机栈和本地方法栈,因此,对于 HotSpot 来说,虽然- Xoss 参数(设置本地方法栈大小)存在,但实际上是无效的,栈容量只由- Xss 参数设定。

如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError 异常。如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出 OutOfMemoryError 异常。

限制于单线程中的操作,尝试了下面两种方法均无法让虚拟机产生 OutOfMemoryError 异常,尝试的结果都是获得 StackOverflowError 异常

在单个线程下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是 StackOverflowError 异常。

2GB内存容量( 操作系统限制)减去 Xmx( 最大堆容量),再减去 MaxPermSize( 最大方法区容量),程序计数器消耗内存很小,可以忽略掉。如果虚拟机进程本身耗费的内存不计算在内,剩下的内存就由虚拟机栈和本地方法栈“瓜分”了。

如果是建立过多线程导致的内存溢出,在不能减少线程数或者更换 64 位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。

1.4.3、方法区和运行时常量池溢出

运行时常量池是方法区的一部分

在 JDK 1.6 及之前的版本中,由于常量池分配在永久代内,我们可以通过- XX: PermSize 和- XX: MaxPermSize 限制方法区大小,从而间接限制其中常量池的容量

" PermGen space", 说明运行时常量池属于方法区( HotSpot 虚拟机中的永久代)的一部分。

而使用 JDK 1.7 运行这段程序就不会得到相同的结果, while 循环将一直进行下去。

在 JDK 1.6 中, intern() 方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串实例的引用,而由 StringBuilder 创建的字符串实例在 Java 堆上,所以必然不是同一个引用

而 JDK 1.7( 以及部分其他虚拟机,例如 JRockit) 的 intern() 实现不会再复制实例,只是在常量池中记录首次出现的实例引用,因此 intern() 返回的引用和由 StringBuilder 创建的那个字符串实例是同一个。

方法区用于存放 Class 的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。

一个类要被垃圾收集器回收掉,判定条件是比较苛刻的。在经常动态生成大量 Class 的应用中,需要特别注意类的回收状况。

1.4.4、本机字节内存溢出

DirectMemory 容量可通过- XX: MaxDirectMemorySize 指定,如果不指定,则默认与 Java 堆最大值(- Xmx 指定)一样

如果读者发现 OOM 之后 Dump 文件很小,而程序中又直接或间接使用了 NIO, 那就可以考虑检查一下是不是这方面的原因。

 

4. 介绍GC 和GC Root不正常引用。

GC(Garbage Collector) roots,特指的是垃圾收集器(Garbage Collector)的对象,GC会收集那些不是GC roots且没有被GC

roots引用的对象。

GC root有几下种:

    • Class - 由系统类加载器(system class loader)加载的对象,这些类是不能够被回收的,他们可以以静态字段的方式保存持有其它对象。我们需要注意的一点就是,通过用户自定义的类加载器加载的类,除非相应的java.lang.Class实例以其它的某种(或多种)方式成为roots,否则它们并不是roots,.
    • Thread - 活着的线程
    • Stack Local - Java方法的local变量或参数
    • JNI Local - JNI方法的local变量或参数
    • JNI Global - 全局JNI引用
    • Monitor Used - 用于同步的监控对象
    • Held by JVM - 用于JVM特殊目的由GC保留的对象,但实际上这个与JVM的实现是有关的。可能已知的一些类型是:系统类加载器、一些JVM知道的重要的异常类、一些用于处理异常的预分配对象以及一些自定义的类加载器等。然而,JVM并没有为这些对象提供其它的信息,因此就只有留给分析分员去确定哪些是属于"JVM持有"的了。

5. 自己从classload 加载方式,加载机制说开去,从程序运行时数据区,讲到内存分配,讲到String常量池,讲到JVM垃圾回收机制,算

法,hotspot。反正就是各种扩展

6. jvm 如何分配直接内存, new 对象如何不分配在堆而是栈上,常量池解析

7. 数组多大放在 JVM 老年代(不只是设置 PretenureSizeThreshold ,问通常多大,没做过一问便知)

8. 老年代中数组的访问方式

9. GC 算法,永久代对象如何 GC , GC 有环怎么处理

10. 谁会被 GC ,什么时候 GC

11. 如果想不被 GC 怎么办

12. 如果想在 GC 中生存 1 次怎么办

13、普遍认为尽量不要使用finalize()进行资源释放,为什么?

在finalize时可能会导致对象复活

finalize的执行时间是没有保障的,它完全由GC线程决定,极端情况下,若不发生GC,则finalize没有机会执行

一个糟糕的finalize会严重影响GC性能

回收对象-->>FinalizerThread的执行队列-->>

但是,在某些场合,使用finalize可以起到双保险作用:在MySQL的JDBC驱动中,com.mysql.jdbc.ConnectionImpl就实现

了finalize

14、GC的两种判定方法:引用计数与引用链

JVM面试的更多相关文章

  1. jvm面试常见题

    背景:jvm相关题目面试必问,后面要深入的进行总结. JVM 面试知识整理 jvm调优命令 调优工具 Minor GC ,Full GC 触发条件 Minor GC触发条件:当Eden区满时,触发Mi ...

  2. JVM(七),JVM面试小知识

    七.JVM面试小知识 1.JVM三大性能调优参数 -Xms -Xmx -Xss 的含义 2.java内存模型中堆和栈的区别 3.不同JDK版本中的intern()方法的区别

  3. Java基础技术JVM面试【笔记】

    Java基础技术JVM面试[笔记] JVM JVM 对 java 类的使用总体上可以分为两部分:一是把静态的 class 文件加载到 JVM 内存,二是在 JVM 内存中进行 Java 类的生命周期管 ...

  4. 【搞定Jvm面试】 Java 内存区域揭秘附常见面试题解析

    本文已经收录自笔者开源的 JavaGuide: https://github.com/Snailclimb ([Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识)如果觉得不错 ...

  5. 【搞定Jvm面试】 JVM 垃圾回收揭秘附常见面试题解析

    JVM 垃圾回收 写在前面 本节常见面试题 问题答案在文中都有提到 如何判断对象是否死亡(两种方法). 简单的介绍一下强引用.软引用.弱引用.虚引用(虚引用与软引用和弱引用的区别.使用软引用能带来的好 ...

  6. 2019年JVM面试都问了什么?快看看这22道面试题!(附答案解析)

    一. Java 类加载过程? Java 类加载需要经历一下 7 个过程: 1. 加载 加载是类加载的第一个过程,在这个阶段,将完成一下三件事情: • 通过一个类的全限定名获取该类的二进制流. • 将该 ...

  7. 【搞定Jvm面试】 JDK监控和故障处理工具揭秘

    本文已经收录自笔者开源的 JavaGuide: https://github.com/Snailclimb ([Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识)如果觉得不错 ...

  8. jvm(n):JVM面试

    Jvm内存结构,一般是面试官对Java虚拟机这块考察的第一问. Java虚拟机的内存结构一般可以从线程共有和线程私有两部分起头作答,然后再详细说明各自的部分,类似树状结构的作答,好处就是思路清晰,面试 ...

  9. [jvm][面试]JVM 调优总结

    https://blog.csdn.net/wfh6732/article/details/57422967 堆大小设置JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-b ...

随机推荐

  1. Oracle 使用sql创建表空间及用户

    create tablespace OrcalDBNamedb datafile 'C:\OracleDBDirc\OrcalDBNamedb.dbf' size 300m; 创建用户create u ...

  2. postfix疯狂外发垃圾邮件

    分析 一.查找main.cf配置文件 localhost# find / -name main.cf /etc/postfix/main.cf 二.打开/etc/postfix/main.cf来看看. ...

  3. jstat undocumented

    jstat -J-Djstat.showUnsupported=true -name btrace.com.sun.btrace.samples.ThreadCounter.count 11674 h ...

  4. qemu -hda /dev/sdc -m 1024 -vga std

    同事拿了个烂u盘让我给他做个启动盘,用cp *.iso /dev/sdc怎么也启动不了. 改用dd if=copied/20140923/debian-7.6.0-amd64-DVD-1.iso of ...

  5. USACO Section 1.1 Friday the Thirteenth 解题报告

    题目 题目描述 黑色星期五是否真的是一件不同寻常的事情?按理来说每个月的13号可能是星期一,或者是星期二...或者是星期天,但是黑色星期五的存在让我们不禁开始猜想,难道每个月的13号刚好是星期五的频率 ...

  6. fopen()函数中参数mode的取值

    FILE * fopen(const char * path,const char * mode); 参数mode字符串则代表着流形态. mode有下列几种形态字符串: r 打开只读文件,该文件必须存 ...

  7. Nginx配置文件常用部分详解

    原文 #定义Nginx运行的用户和用户组 user www www; #nginx进程数,建议设置为等于CPU总核心数. worker_processes ; #全局错误日志定义类型,[ debug ...

  8. 手动启动Android模拟器

    1.5版本中加了个所谓的AVD(Android Virtual Device),AVD就相当于是一个模拟器的,不过你可以利用AVD创建基于不同版本的模拟器,然后使用emulator-avd avdNa ...

  9. CodeForces 605B Lazy Student

    构造.对边的权值排序,权值一样的话,在MST中的边排到前面,否则权值小的排在前面. 然后边一条一条扫过去,如果是1 ,那么连一个点到集合中,如果是0,集合内的边相连. #include<cstd ...

  10. The 2013 ACMICPC Asia Regional Chengdu

    还有19天出发北京站,今年北京站的出题方是上交,去年他们出的成都现场的赛题,首先复盘一下. 去年的成都是我经历的第一次现场赛,也是近距离第一次见到了CLJ的真人,最后也是被虐惨了,那时候是声闻大神带着 ...