1、什么是进程,什么是线程?
进程:操作系统分配资源的最小单元
线程:操作系统调度的最小单元
一个程序至少由一个进程,一个进程至少有一个线程
2、JVM、JRE、JDK的关系?
- JVM:Java虚拟机,能识别.class文件并解析相应指令,最终调用操作系统上的函数,完成相应操作。
- JRE:Java运行时环境,包括JVM及基础类库
- JDK:Java开发工具包,包括JRE,JDK还有编译运行等工具,如:javac、Java、jar等
3、JVM可以运行多种语言吗?
JVM只识别字节码,所以和语言无关。比如Scala、Groovy、Kotlin都可以在JVM上运行。
我们总说跨平台性,不太准确,它是跨语言的。
4、JVM有哪些内存区域?
- 虚拟机栈:在JVM运行过程中存储当前线程运行方法所须的数据、指令、返回值
- 本地方法栈:本地方法栈服务的对象是native方法,和虚拟机栈非常相似
- 堆:JVM中最大的区域,几乎所有的对象都是在堆中存储,(线程共享)
- 方法区:JDK1.7以前叫做
永久代
,JDK1.8叫做元空间
,存放类的信息、常量池、方法数据、方法代码(静态资源) - 程序计数器:记录各个线程执行的字节码的地址
5、堆空间大小怎么配置?各个区域怎么划分?
堆空间分为新生代、老年代、永久代。
区域划分可以按照活跃数据来判断:
空间 | 倍数 |
---|---|
总大小 | 3-4倍活跃数据大小 |
新生代 | 1-1.5倍活跃数据大小 |
老年代 | 2-3倍活跃数据大小 |
永久代/元空间 | 1.2-1.5倍Full GC后的永久代空间占用 |
比如:活跃数300M,那么:
- 总的300 × 4 = 1200M
- 新生代300 × 1.5 = 450M;
- 老年代-->总-新生代:750M
- 永久代/元空间:300M
6、JVM中哪些内存区域会发生内存溢出(OOM)?
栈溢出:StackOverFlowError
方法区溢出:OutOfMemoryError Metaspace(元空间)
堆溢出:OutOfMemoryError Java heap space(堆空间)
本地直接内存溢出:OutOfMemoryError Direct buffer memory(直接内存)
7、JVM在创建对象时采用了哪些并发安全机制?
也就是两个线程同时创建对象,如果没有并发安全的处理,可能A创建完,B把它修改了,那么就会造成A读到的不对了。
CAS+失败重试
线程1在读取该块内存空间的时候还没有分配给对象,就,然后比较一次以防止预处理过程中有线程(如线程2)抢占了该块空间,如果读到值不为null,即不相等,就再读取一次,如果这时候有值说明空间已被抢占了,就寻找下一块空间,否则,分配此块空间给线程创建的对象。
本地线程分配缓冲
本地线程分配缓冲(Thread Local Allocation Buffer):
每个线程在Java堆中预先分配一小块私有内存,也就是本地线程分配缓重,这样每个线程都有单独的buffer,如果要分配内存,就在自己的buffer上分配,这样就不存在竞争的情况,可以大大的提升分配效率
8、什么是对象头?对象头里有哪些东西?
9、为什么不要使用finalize方法?
一个对象要被回收,要经过两次标记过程,如果没找到GC Root的引用链会进行第一次标记。随后会进行筛选,可以在finalize方法中拯救对象,使它变为存活对象。
为什么不使用finalize方法:
- finalize方法执行效率非常低
- finalize方法只能执行一次
protected void finalize() throws Throwable{
super.finalize();
System.out.println("finalize method executed");
类.对象 = this;
}
本来把对象 = null了,也就是变成了垃圾,上面finalize方法把对象的引用又接上了。上面这些就是说明finalize方法执行效率非常低,中间可能有其他操作,造成问题。
10、怎么判断对象存活?
- 引用计数法:有对象引用就 + 1,断了就 - 1,最后判断计数器是否为0;这种方式不能判断循环引用的情况
- 可达性分析法:设置一些GC Root:静态变量、线程栈变量、常量池、JNI(指针)等等,通过它们为根判断某对象是否有引用链路
11、什么是复制算法?
先标记存活对象,然后把存活的对象复制到另一块内存区域中。
优缺点:
- 效率很高
- 内存空间利用率很低
12、什么是标记清除算法?
先标记存活对象,然后把没标记的清除掉。
优缺点:
- 会产生很多内存碎片
13、什么是标记整理算法?
标记完存活对象,移动存活对象,然后从最后一个存活对象开始,往后都清除掉。
优缺点:
- 没内存碎片
- 涉及对象移动,效率较低
14、扩容新生代为什么能提高GC的效率?
- 新生代空间X:每隔5s发生1次GC,每次GC耗时100ms
- 新生代空间2X:每隔10s发生1次GC,每次GC耗时100ms
整个新生代垃圾回收的时间:T1 + T2;而空间一旦扩大,T1扫描时间会变长,复制的时间也会变长,可是要考虑一个事情,就是如果一个对象存活时间5 < t < 10,扩大新生代,也就延长了GC周期,延长了扫描的时间,一部分不是垃圾的对象,这个周期内可能变成了垃圾,也就是让复制某些对象的过程没有了。
15、阐述CMS垃圾回收器?
并发的标记清除算法(Concurrent Mark Sweep),专门回收老年代。
四个阶段:
- 初始标记:标记与GC Roots直接关联的对象
- 并发标记:时间比较长,不暂停业务线程,但这时不暂停业务线程会导致业务线程可能会在并发标记阶段修改引用对象
- 重新标记:暂停业务线程,再看一下引用的变化,有调整的这个时间调整
- 并发清理:标记清除算法进行清除
CMS的问题:
- CPU核心数太少不适合CMS垃圾回收
- 并发清理阶段会产生浮动垃圾,只能等到下一次GC的时候才能回收
- 标记清除算法会产生内存碎片
16、什么是守护线程、用户线程?
java中的线程包括:守护线程和用户线程。
守护线程可以通过Thread.setDaemon(true)
设置,守护线程一般在垃圾回收的适合使用。
使用守护线程的注意点:
Thread.setDaemon(true)
必须在Thread.start()
之前调用,否则运行时会抛出异常- 守护线程会在所有用户线程结束的时候停止
17、什么是多线程的线程上下文切换?
CPU时间片:CPU时间片是CPU给每个线程执行的时间段,一般就是几十毫秒
上下文切换:一个线程的时间片执行完毕后,或者被迫暂停了,这种情况下,另一个线程会被操作系统选中执行,这样一个线程被剥夺使用权,另一个线程开始执行的过程叫做上下文切换
上下文:切入切出的过程中,操作系统需要记录相应的进度信息,这就是上下文
18、什么是死锁?死锁的危害?
死锁:两个或两个以上的线程在执行过程中,因为竞争资源而造成相互等待的现象,如果没有外力干预,线程都无法执行下去
危害:
- 死锁会让进程不能得到正确的结果
- 死锁会使资源利用率降低
- 死锁还容易造成新的死锁
19、Java的Executor和Executors的区别?
JDK常用的三种线程池:
- newSingleThreadExecutor
- newCachedThreadPool
- newFixedThreaPool
区别:
- Executor 接口对象能执行我们的线程任务;
- Executors 工具类的不同方法按照我们的需求创建了不同的线程池,来满足业务的需求。
- ExecutorService 接口继承了Executor接口并进行了扩展,提供了更多的方法,我们能够获得任务执行的状态并且可以获取任务的返回值。
20、什么是CAS操作,缺点是什么?
CAS叫做compare and swap,比较并替换,线程会记录旧值,并且会和现在要操作的实时值进行比较,如果是预期的值就进行交换,如果不是预期的值,那么就再重新进行比较。
CAS问题:
- 因为要不断循环比较,开销比较大
- 只能确保一个共享变量
- ABA问题
ABA问题:如果某个线程把Object对象类型(总而言之不是基本类型)的数据从A改为B又改为A,B线程进行compare发现和预期的一样,还是A,但实际上是被修改过了
21、Lock接口是什么?它比synchronized有哪些优势?
Lock接口比同步方法和同步块更具有扩展性的锁操作。
Lock接口属于synchronized的扩展版,Lock提供了无条件的,可轮询的(tryLock)、定时的(tryLock带参方法)、可中断的(lockInterruptibly)、可多条件队列的(newCondition)锁操作。
另外Lock实现的类基本都支持非公平锁和公平锁,synchronized只支持非公平锁,不过非公平锁更加高效。
22、什么是阻塞队列?及其实现原理?
阻塞队列是一个支持两个附加操作的队列。
附加操作是:在队列为空的时候,获取元素的线程会等待队列变为非空。当队列满了时,存储元素的线程会等待队列可用。
阻塞队列在实现上,主要是利用了Condition和Lock的等待通知模式。
BlockingQueue接口继承了Queue接口。
23、什么是Callable和Future?
Callable接口类似Runnable,但Runnable不能有返回值,并且无法抛出返回结果的异常。
而Callable更加强大,可以返回值,这个返回值被Future拿到,Future可以拿到异步执行任务的返回值。
Callable可以认为是一个带有回调的Runnable。
Future接口表示异步任务,是还没有完成的任务给出的未来结果,所以说Callable用于产生结果,Future用于获取结果。
24、什么是FutureTask?
FutureTask是一个可以取消的异步运算,他有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候才能取回,如果运算尚未get方法将会阻塞。
一个FutureTask对象可以对调用了Callable和Runnable的对象进行包装,由于FutureTask也是调用了Runnable接口所以它可以提交给Executor来执行。
25、什么是并发容器的实现?
并发容器可以简单的理解为通过synchronized来实现同步的容器,如果有多个线程调用同步容器的方法,它们将会串行执行。比如Vector、HashTable、Collections.synchronizedSet、synchronizedList等方法返回的容器。
上面都是一些简单的,还有一些复杂的比如ConcurrentHashMap等。
26、为什么调用start()方法会执行run()方法,而不直接调用run()方法?
当调用start()方法时,将创建新的线程,并且执行在run()里面的艾玛。
但是如果直接调用run()方法,是不会创建新的线程的,也不会调用线程的代码,只会把run()方法当作普通方法来执行。
27、什么是不可变对象,它与并发应用有什么帮助?
不可变的对象(immutable Objects)一旦创建出来,它的状态(数据、属性值)就不能修改。
Java平台提供了很多不可变的类,比如String、基本类型的包装类、BigInteger、BigDecimal等等
不可变的对象是线程安全的,因为它们的值根本不会改变。
28、乐观锁和悲观锁如何理解?怎么实现?
乐观锁:每次拿数据认为别人不会修改,所以不会上锁,只有在更新的时候判断一下有没有人更新数据,可以使用版本号等机制来实现。一种实现方式就是CAS
悲观锁:每次拿数据都认为别人会修改,所以都会加锁,这样别人想拿锁只能等到释放锁以后,比较常见的就是Lock和synchronized
29、wait和sleep方法的区别?
wait方法释放锁、sleep方法不释放锁。用途也不同,wait方法用于线程交互、sleep用于线程暂停。
30、为什么wait、notify、notifyAll方法不在Thread类中?
Java提供的锁是对象级别而不是线程级别。
每个对象都有锁,通过线程获得,如果线程需要等待某些锁那么调用对象中wait方法就有意义了。
如果wait方法定义在Thread类中,那么线程正在等待的是哪个锁就不明显了。
31、什么是Java内存模型?
计算机多级缓存架构
Java内存模型
几大问题:
- 可见性:线程1处理变量x,线程2也要处理x,那么它们之间怎么通信的问题,可以通过volitile关键字
- 安全性:两个线程同时处理一个变量,容易发生安全问题,加锁或cas
- 重排序:指令的乱序执行
32、什么是线程安全?
当多个线程访问某个类,不管运行时环境采用何种调度方式,或者这些线程如何交替执行,并且在调度代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为,这就是线程安全。
怎么保证?
- 不可变(immutable object)
对于一些封装类型等等,都是final类型,也就是说生来就是不可变的,这样能保证安全性
- 加锁和CAS
一般就是采用加锁的方式,不管是synchronized还是lock,也可以使用原子变量,使用CAS机制
33、一个线程如果出现了运行时异常会怎么样?
如果该异常没有被捕获,那么线程就停止了
如果该异常被捕获了,那么线程继续执行
注意:如果一个线程持有某个对象的监视器,那么这个对象监视器会被立即释放
34、如何在两个线程之间共享数据?
static
定义数据:
private static int data = 0;
这里会有线程安全问题
35、volatile有什么作用?
- 确保读线程可见性
- 禁止指令重排
36、ThreadLocal有什么用?
ThreadLocal是Java中一种特殊的变量,每个线程都有一个ThreadLocal,也就是说每个线程都拥有自己独立的一个变量,竞争条件被彻底消除了。
37、生产者消费者模型的作用是什么?
- 通过平衡生产者的生产能力和消费者的消费能力,提升整个系统的运行效率(主要作用)
- 解耦,使生产者和消费者的联系变少(附带作用)
38、为什么要使用线程池?
- 节约资源:避免频繁地创建和销毁线程,因为创建和销毁线程需要向操作系统去申请,使用线程池达到线程对象的重用
- 灵活:使用线程池还可以根据项目灵活地控制并发的数目
39、怎么唤醒一个阻塞的线程?
- wait和notify
- park和unpark:是LockSupports的一个工具类,里面包含这两个方法
40、Java中用到的线程调度算法是什么?
抢占式。
一个线程用完CPU之后,操作系统会根据优先级、线程饥饿情况等算出一个总优先级并分配下一个时间片给某个线程执行。
41、什么是线程组,为什么Java中不推荐使用?
线程组不是线程池。线程组是为了方便管理线程。线程池是为了方便管理线程的生命周期、复用线程、减少创建销毁线程的开销。
线程组ThreadGroup对象中的stop、resume、suspend会导致安全问题,主要是死锁问题,已经被官方废弃了。
线程组不是线程安全的,在使用过程中不能及时获取安全的信息。
42、Java获取线程的方式?
- 继承Thread类
- 实现Runnable接口
- Callable和FutureTask
- 线程池创建线程
43、SynchronizedMap和ConcurrentHashmap的区别?
SynchronizedMap一次性所著整张表来保证线程安全,所以每次只能有一个线程来访问map。
ConcurrentHashmap使用分段锁来保证在多线程下的性,把整个map分割成很多个segment,并发性能好。
强一致性:任何时候线程读取到的数据缓存都一样,SynchronizedMap属于这种
弱一致性:不能保证任何一次读都能读到最近一次写入的数据,但能保证最终可以读到写入的数据,ConcurrentHashmap属于这种
44、ConcurrentHashmap的并发度是什么?
JDK1.7
ConcurrentHashmap把实际map划分成若干部分来实现它的可扩展性和线程安全,这种划分是使用并发度获得的,它是ConcurrentHashmap类构造函数的一个可选参数,默认值16,这样可以在多线程的情况下避免争用。
JDK1.8
它摒弃了使用segment(锁段)的概念,而是启用一种全新的方式实现,利用CAS算法,同时加入了更多的辅助变量来提高并发度
45、CopyOnWriteArrayList可以用于什么场景?
CopyOnWriteArrayList是JUC包提供的方法,它实现了读操作无锁,写操作通过操作底层数组的新副本来实现,是一种读写分离的并发策略。
适用于读的非常频繁而写
46、Thread.sleep(0)的作用是什么?
操作系统采用抢占式的线程调度算法,而有时候会出现某些线程总是获取到cpu的控制权,为了让优先度不那么高的线程也有机会获取cpu,那么就可以使用Thread.sleep
来触发一次系统分配时间片的操作,也就是让其他线程有机会获取cpu,避免线程假死的情况发生。
47、Thread.yield方法有什么用?
线程的五种状态:
- NEW
- RUNNABLE
- RUNNING
- BLOCKED
- DEAD
而yield方法是使当前线程从RUNNING状态变为RUNNABLE状态。
48、什么是线程调度器和cpu时间片?
线程调度器是一个操作系统的服务,它负责为RUNNABLE状态的线程分配时间片,一旦创建一个线程并启动,便依赖线程调度器。
CPU时间片是CPU分配给每一个线程的执行时间段,一般是几十毫秒。把可用的CPU时间分配给可用的RUNNABLE线程,基于优先级或等待时间分配。
49、如何确保main()线程是Java中最后结束的线程呢?
可以使用Thread类中的join方法来确保所有程序创建的线程在main方法强结束。
50、什么是Java Timer类?如何创建一个有特定时间间隔的任务?
java.util.Timer类是一个工具类,可以用于安排一个线程在未来的某个特定的时间执行。Timer类可以用安排一次性任务或者周期任务。
可以使用java.util.TimerTask是一个实现类Runnable接口的抽象类,需要去继承这个类来创建自己的定时任务并使用Timer去安排它的执行。
51、Semaphore是什么?
Semaphore是一个信号量,作用是限制某段代码块的并发数。
Semaphore有一个构造函数,可以传入一个int型整数n,表示某段代码最多只有n个线程可以访问,如果超出了n,那么就等待某个线程执行完后再进入。
如果Sempahonre构造函数传入的是1,那么相当于是synchronized方法了。
52、为什么代码会重排序?
什么是代码重排序?就是在不影响单线程执行结果的前提下,计算机为了最大限度的发挥机器性能,会对指令重排优化。
53、假设三个线程T1,T2,T3,怎么保证它们按顺序进行?
可以使用join方法来保证它们的顺序执行
54、如果提交任务时,线程池队列满了,会发生什么?
当核心线程数和最大线程数相同,线程池满了,会任务拒绝
当核心线程数小于最大线程数,那么还会创建非核心线程来执行任务,当超过的时候,会任务拒绝
55、ReadWriteLock是什么?
ReadWriteLock接口实现是ReentrantReadWriteLock类。
ReentrantLock等大部分锁都是排他锁,这些锁在同一时刻只允许一个线程来访问。而读写锁同一时刻可以有多个读线程访问,但是在写的时候,所有读线程都被阻塞。读写锁维护了一对锁,读锁和写锁,实现读写分离,性能有所提升。
一般情况下,读写锁的性能都比排他锁好,因为大多数情况都是读,在读多于写的情况下,读写锁能够比排他锁有更好的并发性和吞吐量。
56、volatile和atomic的不同?
volatile变量可以确保可见性和指令重排,不能保证原子性,比如修饰count变量,本身count++不具备原子性,加了也不能保证。
AtomicInteger类提供的atomic方法可以让这种操作具有原子性,如getAndIncrement()方法会原子性的进行增量操作,把当前值+1,其他数据类型也一样。
57、CyclicBarrier和CountDownLatch的区别?
CountDownLatch是一个同步工具类,用来协调多个线程之间的同步。
这个工具常用来控制线程等待,可以让某一个线程等待直到倒计时结束,再开始执行。
CyclicBarrier和CountDownLatch非常相似,可以实现线程间的技术等待。
区别:CyclicBarrier可以重复使用,CountDownLatch不能重复使用
58、Java中如何获取线程dump文件?
- 使用Java自带的工具VisualVM来实现
- jmap -dump:format=b,file=F:/地址 pid
59、怎么检测一个线程是否持有对象监视器?
Thread.holdLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true。
注意这是static方法,某条线程指的就是当前线程。
60、linux环境下如何查找哪个线程使用cpu最长?
top先查看进程
然后top -p 进程号
这样就能找到是哪个线程占用cpu最高
61、什么是AQS?
AQS是AbstractQueuedSynchronizer,是构建锁或者其他同步组件的基础框架,比如ReentrantLock、RentrantReadWriteLock和CountDownLatch就是基于AQS实现的。
其中使用了int类型表示同步状态,通过内置的队列完成资源获取线程的排队工作,它可以实现两种同步方式:独占式、共享式。
AOS的主要使用方式是继承,子类继承AQS并实现它的抽象方法来管理同步状态,同步器的设计基于模板方法模式,需要覆盖其中的tryAcquire
、tryReleaseShared等方法。
62、同步方法和同步块哪个是更好的选择?
同步方法相当于锁住了this,范围是整个方法;当我们不需要是锁住整个方法,而是方法的部分的时候,就可以使用同步块。
63、多线程同步和互斥有几种实现方法?
- 临界区:适合一个进程内的多个线程访问公共区域或代码段时使用
- 互斥量:适合不同进程内多线程访问公共区域或代码段时使用
- 事件:通过线程间触发事件实现同步互斥
- 信号量:可以实现多个线程同时访问公共区域数据,原理与操作系统中PV操作雷系,先设置一个访问公共区域的线程最大连接数,每有一个线程访问共享资源数就减一,直到资源数小于0
64、如何实现多线程之间通讯和协作?
线程与线程之间是需要通信和协作的,实现的机制:
- synchronized + wait + notify模式
- lock + Condition模式
65、为什么wait和notify方法要再同步块中调用?
不这样做会抛IllegalMonitorStateException异常。
wait/notify是线程之间的通信,存在竞争态,必须满足条件才能进行,不加锁,就不满足条件。
66、JVM哪个参数是用来控制线程栈之间的大小?
-Xss 每个线程的栈的大小,默认大小是1m。
67、什么是阻塞式方法?
阻塞式方法是指程序会一直等待该方法完成期间不做任何其他的事儿。
68、对象的创建过程?
- 检查加载
首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用(也就是用一组符号来描述所引用的目标),并且检查类是否已经被加载、解析初始化过。
- 分配内存
虚拟机将会为新生对象分配内存,等于是在堆中划分一块内存
- 内存空间初始化
内存分配过后,虚拟机需要将分配到的内存空间都初始化为零值,保证对象实例在java代码中不用赋初始值也能直接用
- 设置
虚拟机堆对象进行必要的设置,比如这个对象是哪个类的实例,如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等。这些信息保存在对象的对象头中
- 对象初始化
进行真正的初始化操作
69、MinorGC和FullGC分别在什么适合发生?
当新生代满了的时候会发起一次Minor GC
老年代空间满了会发生Full GC
如果方法区满了也会发生Full GC
70、什么是类加载器,类加载器有哪些?
类加载过程
类从被加载到虚拟机内存开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。
-
加载阶段
- 通过类的全限定类名获取这个类的二进制字节流
- 将类字节流的静态存储结构转化为方法区的运行时数据结构
- 在内存当中生成这个类的java.lang.Class对象,并作为在方法区该类数据的访问入口
-
验证阶段
验证类的字节流是否符合JVM虚拟机的规范。
主要检查的是:
文件格式验证:判断当前字节流是否符合Class文件格式要求
元数据验证:判断数据类型是否符合Java语言规范
字节码验证:判断方法是否符合Java语言规范
符号引用验证:判断方法是否符合Java语言规范
-
准备阶段
为类中静态变量赋初始值,类变量分配空间是在准备阶段完成,赋值是在初始化中执行
final修饰的静态变量如果是基本类型,在准备阶段就赋值了,值就被定下来了
final修饰的静态变量如果是引用类型,赋值也是在初始化阶段完成
-
解析阶段
将常量池中的符号引用变为直接引用
-
初始化阶段
初始化类的最后一步,也就是调用方法
类加载器
- 启动类加载器:加载JVM自身需要的类,它会把JAVA_HOME/lib下的核心类库加载进来
- 扩展类加载器:这个类加载器加载的是以前SUN公司JAVA_HOME/lib/ext的目录
- 应用程序类加载器:加载getClassLoader()中加载的类,也就是系统类的加载器
- 自定义类加载器:就是自定义的类
71、类加载器双亲委派机制?
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式,即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己想办法去完成
为什么要采用双亲委派机制
是为了避免重复加载类,如果父类加载器加载过该类,子类加载器就不再加载了。
72、如何打印JVM日志?
一般只要开启gc日志打印,都会默认开启简单日志模式,生产环境建议开启详细gc日志模式:
-XX:+PrintGC //简单gc日志模式
-XX:PrintGCDetails //详细gc日志模式
73、介绍常见的JVM参数?
标准的:以
-
开头,所有的HotSpot都支持
保证Java虚拟机的所有实现都支持标准选项
非标准的:
-X
开头,特定版本HotSpot支持特定命令
非标准,不是所有的HotSpot都支持。比如设置堆空间大小等等操作
高级选项:
-XX
开头
开发人员选项,需要特定权限才行
74、G1垃圾回收器的特点?
- 并发与并行处理
- 分代收集
- 空间整合,基于标记-整理算法,解决了内存碎片化的问题
- 可以建立可预测的停顿模型
- 将整个java堆内存模型划分为多个大小相等的Region,使得老年代和新生代不再隔离开来