Java 大厂面试集合
JUC
volatile
并行:同时做A,B两件事
并发:同时有很多线程访问某个资源
volatile: 是java虚拟机提供的轻量级的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令冲排序
JMM
Java内存模型:Java Memory Model,它描述的是一组规范或者规范定义了内存中各个变量。主内存是共享内存区域,线程对变量操作,会先把数据读取到自己的工作内存中,然后对变量进行操作,然后写回主内存,各个线程的工作内存中存储着主内存的变量副本拷贝,线程间通信必须通过主内存来完成。
- 可见性
- 原子性:volatile i++ 是会导致多线程问题的
- 有序性:禁止指令重排
指令重排序
- 编译器优化的重排
- 指令并行的重排
- 内存系统的重排
编译器为了保证高效,会把代码顺序重排序,单线程中会保证最终结果和代码顺序执行结果一致。多线程中可能会导致出现问题。两个语句没有数据依赖性的时候,可能会导致指令重排。
如果在指令间插入一条 Memory Barrier,会禁止内存屏障禁止在内存屏障前后的指令执行重排序优化。Memorry Barrier 会强制刷新CPU缓存数据。
哪些地方使用了 volatile
双重检测的单例模式
class type5 {
//懒汉式 线程安全 double check
private static volatile type5 instance;
private type5() {}
public static type5 getInstance() {
if (instance == null)
synchronized (type5.class) {
if (instance == null)
instance = new type5();
}
return instance;
}
}
这里synchronized块能保证每次只有一个线程能进行 new 操作,这就是保证了可见性。volitile保证的是禁止指令重排序。
new 对象 可以分为三个步骤:
- memory = allocate() 分配内存
- instance(memory) 实例化对象
- instance = memory 实例化后 把引用指向内存区域
但是2,3两步是没有数据依赖的,可以指令重排序,也就说可以先让 instance引用不为 null,再实例化对象。这个时候别的线程得知 instance 不为null,直接返回了:引用不为null,但是内存中的对象还没实例化。
CAS
AtomicInteger 类型就能保证原子性,底层原理是 CAS 乐观锁。CAS是一个系统原语,由若干条指令组成,执行过程不能被中断,CAS就是一条cpu指令,不会造成数据不一致。
Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。Unsafe类提供了硬件级别的原子操作,提供了一些绕开JVM的更底层功能,由此提高效率。
内存屏障
public native void loadFence();
public native void storeFence();
public native void fullFence();
loadFence:保证在这个屏障之前的所有读操作都已经完成。
storeFence:保证在这个屏障之前的所有写操作都已经完成。
fullFence:保证在这个屏障之前的所有读写操作都已经完成。
比较并交换:如果主内存值和expect相同,就试着把他设置成目标值并通知其他线程修改值,否则操作失败。
CAS中的 getAndIncrement,可以认为,获取当前对象地址值,然后比较并交换,失败就一直重试,自旋直到成功。
CAS的缺点:
- 如果cas失败,会一直尝试,如果长时间失败,会造成很大开销。
- 只能保证一个共享变量的原子操作
- ABA问题
ABA问题
由于CAS有两步操作,获取值,然后比较并设置写入值(这个中间可能其他线程会反复修改这个值,可能会改回前者期望的值)。两个线程可能会出现:A -> B -> A
一个线程 A -> B,一个线程 B -> A
原子引用
AtomicReference:
public class appointment {
public static void main(String[] args) throws Exception {
AtomicReference<User> atomicReference = new AtomicReference<>();
User z3 = new User("z3", 22);
User l4 = new User("l4", 25);
atomicReference.set(z3);
System.out.println(atomicReference.compareAndSet(z3, l4) + " " + atomicReference.toString());
System.out.println(atomicReference.compareAndSet(z3, l4) + " " + atomicReference.toString());
}
}
@ToString
@Getter
@Setter
@AllArgsConstructor
class User {
String name;
int age;
}
输出如下:
true User(name=l4, age=25)
false User(name=l4, age=25)
规避ABA问题,需要添加版本号。可以使用 AtomicStampedReference,带有时间戳的原子引用。
public class appointment {
static AtomicStampedReference<Integer> asr = new AtomicStampedReference<>(100, 1);
public static void main(String[] args) throws Exception {
new Thread(() -> {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace(); }
asr.compareAndSet(100, 101,1,2);
asr.compareAndSet(101, 100,2,3);
}, "t1").start();
new Thread(() -> {
try {
int stamp = asr.getStamp();
Thread.sleep(1000);
System.out.println(asr.compareAndSet(100, 2019, stamp, 1 + stamp) + " " + asr.get(new int[]{1}));
} catch (InterruptedException e) {
e.printStackTrace();
} }, "t2").start();
}
}
这个操作分为两步,获取版本号,然后比较和交换设置值,通过对比版本号,保证自己在操作过程中没有其他线程操作。思路和前面的描述很接近。
阻塞队列
由数组结构组成的有界阻塞队列。一个定长的队列,如果放满了,往里面添加元素的操作将会被阻塞,如果为空,拿元素也会阻塞。
LinkedBlockingQueue:默认最大值 Integer.MAX_VALUE:
SynchronousQueue:里面只放一个元素,单个元素队列。
JVM 参数调优
-XX:+或者-表示开启或者关闭某个参数
jps 等价于 ps 查看后台进程
[root@centos02 java]# java demo
Hello world
这个demo是打印一句话,同时用CountDownLatch阻塞。
jinfo 查看具体进程细节
[root@centos02 ~]# jinfo -flags 2069
Attaching to process ID 2069, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.261-b12
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=31457280 -XX:MaxHeapSize=478150656 -XX:MaxNewSize=159383552 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=10485760 -XX:OldSize=20971520 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
Command line:
一组例子:启动 PirntGCDetails
[root@centos02 java]# java -XX:+PrintGCDetails demo
Hello World
[root@centos02 ~]# jinfo -flag PrintGCDetails 2470
-XX:+PrintGCDetails
[root@centos02 java]# java -XX:MetaspaceSize=300000 demo
Hello world
说名起了作用,默认是 21807104
[root@centos02 ~]# jinfo -flag MetaspaceSize 2788
-XX:MetaspaceSize=299008
-Xms :-XX:InitialHeapSize 默认 1 / 64
-Xmx :-XX:MaxHeapSize 默认 1 / 4 (生产设置成一样)
java -XX:+PrintFlagsInitial //很重要,jvm初始化参数
java -XX:+PrintFlagsFinal -version //最终修改后的jvm,和版本号
列出来的值,如果是 = 表示没修改过,如果是 := 表示被我们人为修改过。
综合起来:
java -XX:+PrintFLagsFinal -Xms30m demo
-XX:+PrintCommandLineFlags : 打印出默认GC垃圾回收器
[root@centos02 java]# java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=29808384 -XX:MaxHeapSize=476934144 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
java version "1.8.0_261"
Java(TM) SE Runtime Environment (build 1.8.0_261-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, mixed mode)
常见参数:
栈管运行,堆管存储
-Xss = -XX:ThreadStackSize //单个线程栈的大小,默认 512k - 1024k。linux默认是 1024 k
[root@centos02 ~]# jinfo -flag ThreadStackSize 3543
-XX:ThreadStackSize=1024
-Xmn = //默认不需要调,设置年轻代大小
-XX:MetaspaceSize //元空间和永久代类似,都是jvm规范中方法区的实现,元空间不再虚拟机中,而是使用本地内存。21807104 约等于 21 mb,容易OOM,可以配置大一些。512m-1024m
-XX:PrintGCDetails // 打印出详细垃圾回收日志
-XX:SurvivorRatio=8, Eden:S0:S1=8:1:1
[root@centos02 java]# java -XX:SurvivorRatio=6 demo
Hello world
[root@centos02 ~]# jinfo -flag SurvivorRatio 3819
-XX:SurvivorRatio=6
-XX:NewRatio //配置新生代和老年代在堆内存中的占比,年轻代占整个堆的 1/3 (如果为2)
-XX:MaxTenuringThreshold. //放入养老区之前的最多次数
引用(强软弱虚)
强引用
当内存不足的时候,即使出现了OOM也不会对该对象进行回收。只要还有强引用指向一个对象,垃圾收集器就不会碰这个对象。普通一个对象赋值给一个引用变量,这个引用变量就是一个强引用,处于可达状态。是Java内存泄漏的主要原因。
软引用
内存足够的前提下,不收垃圾,内存不够了就会回收软引用对象。需要用到 java.lang.ref.SoftReference,用在对内存敏感的程序中,比如高速缓存就有用到软引用,内存足够多就保留,不够就回收。
public static void softRefEnough() {
Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference<>(o1);
System.out.println(o1);
System.out.println(softReference.get());
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(softReference.get());
}
这种情况下 软引用不会被回收,输出如下:
java.lang.Object@5a10411
java.lang.Object@5a10411
null
java.lang.Object@5a10411
如果是软引用:
public static void softRefNotEnough() {
Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference<>(o1);
System.out.println(o1);
System.out.println(softReference.get());
o1 = null;
System.gc();
System.out.println(o1);
try {
List<byte[]> ls = new ArrayList<>();
for (int i = 0; i < 100; ++i) {
byte[] bytes = new byte[50 * 1024 * 1024];
ls.add(bytes);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(softReference.get());
}
}
这样内存不够的时候是会回收的,输出如下:
java.lang.Object@5a10411
java.lang.Object@5a10411
null
null
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.xxx.springboot.appointment.appointment.softRefNotEnough(appointment.java:33)
at com.xxx.springboot.appointment.appointment.main(appointment.java:46)
弱引用
弱引用只要回收内存,一定会被回收。
public static void softRefNotEnough() {
Object o1 = new Object();
WeakReference<Object> weakReference = new WeakReference<>(o1);
System.out.println(o1);
System.out.println(weakReference.get());
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(weakReference.get());
}
输出:
java.lang.Object@5a10411
java.lang.Object@5a10411
null
null
软引用适合高速缓存,如果内存中要存入很多图片来加速,可以使用软引用,防止OOM。
HashMap<String, SoftReference<BitMap>> imageCache = new HashMap<String, SoftReference<BitMap>>();
new CountDownLatch(3).await();
虚引用
PhantomReference
在任何时候都会被垃圾回收,形同虚设,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么他就和没持有引用一样,任何时候都会被垃圾回收,也不能单独使用,也不能通过它访问对象,必须配合引用队列使用,ReferenceQueue。
虚引用的唯一目的:对象被收集器回收的时候会收到一个系统通知或者添加进一步的处理。
引用队列:ReferenceQueue,
public static void main(String[] args) throws Exception {
Object o1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
WeakReference<Object> weakReference = new WeakReference<>(o1, referenceQueue);
System.out.println(o1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
o1 = null;
System.out.println("***************");
System.gc();
Thread.sleep(500); // 保证gc完成
System.out.println(o1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
}
输出:GC后,虚引用GC前把对象放入引用队列中。
java.lang.Object@5a10411
java.lang.Object@5a10411
null
***************
null
null
java.lang.ref.WeakReference@2ef1e4fa
WeakHashMap
public static void main(String[] args) throws Exception {
HashMap<Integer, String> map = new HashMap<>();
Integer key = new Integer(3);
String value = "value";
map.put(key, value);
System.out.println(map);
key = null;
System.out.println(map);
System.gc();
System.out.println(map);
new CountDownLatch(3).await();
}
输出如下:
{3=value}
{3=value}
{3=value}
hashMap中是一个node节点,所以说是不会被回收的。
换成WeakHashMap,输出如下:
public static void main(String[] args) throws Exception {
WeakHashMap<Integer, String> map = new WeakHashMap<>();
Integer key = new Integer(3);
String value = "value";
map.put(key, value);
System.out.println(map);
key = null;
System.out.println(map);
System.gc();
System.out.println(map);
new CountDownLatch(3).await();
}
{3=value}
{3=value}
{}
对于WeakHashMap,会被回收。
OOM
StackOverflowError
递归特别多,栈爆了,是属于Error
Java heap space
堆满了,也属于Error
GC overhaead limit exceeded
这是GC回收时间过长泡出的错误,超过98%的时间来做GC回收且回收了不到2%的堆内存,连续多次GC只回收了不到2%的内存,如果不抛错误,会迫使GC再次执行,就形成恶性循环。
Direct buffer memory
直接内存崩溃。NIO的时候会用native方法分配内存,是在堆内存之外的,不会被GC回收(速度快),本地内存(方法区,元空间)满了,导致程序崩溃。
Unable to create native thread
linux系统默认允许单个进程创建1024个线程,超过这个数量就会爆出这个错误
线程不能超过linux服务器线程上限
[root@centos02 ~]# ulimit -u
7184
Metaspace
元空间被用完了,默认初始值约为21mb。
可以使用动态代理把元空间撑爆。
垃圾回收器
串行
Serial
为单线程环境设计,只有一个线程回收垃圾,会暂停所有工作(stop the world | STW)
并行
Parallel,java8默认使用的GC。
多个垃圾收集器线程并行计算,此时用户线程是暂停的,适用于科学计算和弱交互场景。
这里有两种,一种是ParallelGC另一种是ParallelOldGC。
CMS
并发:ConcMarkSweep:并发标记清除
用户线程和GC线程同时执行,多适用于互联网公司,不需要暂停用户。
G1
把堆内存划分成不同区域,然后并发地对其进行垃圾回收。
[root@centos02 ~]# jinfo -flag UseParallelGC 19879
-XX:+UseParallelGC
[root@centos02 ~]# jinfo -flag UseSerialGC 19879
-XX:-UseSerialGC
关键词解释:
- DefNew: Defalut New Generation
- Tenured: Old
- ParNew:Parallel New Generation
- PSYoungGen:Parallel Scavenge
- ParOldGen:Parallel Old Generation
新生代使用
-
Serial Copying:串行垃圾回收器,复制算法。
对应JVM参数:-XX:+UseSerialGC,开启后会使用 Serial + Serial Old 组合。
-
Parallel Scavenge:并行清除。新生代,老年代都使用并行多线程垃圾回收器。
吞吐量(多少时间用来工作而非垃圾回收 / 总时间),能高效利用CPU的时间,PS还能自适应调节最适合的停顿时间(-XX:MaxGCPauseMillis)或最大吞吐量。
使用 -XX:+UseParallelGC 或 -XX:+UseParallelOldGC 都会使得新生代和老年代使用并行GC回收。
-
ParNew:追求高吞吐量,适用于科学计算(Parallel New Generation)新生代默认GC收集器。对应JVM参数:-XX:+UseParNewGC,启用后只影响新生代,会使用 ParNew + Serial Old 组合,JVM不建议这种组合。ParNew可以设置收集线程数:-XX:ParallelGCThreads=4。会Stop the world。
[root@centos02 java]# java -XX:+UseParNewGC demo Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release Hello world [root@centos02 ~]# jinfo -flag UseParNewGC 42219 -XX:+UseParNewGC
-
G1:
老年代使用
-
Serial MSC:(Serial Old)几乎不用了。单线程整理算法。是CMS的备用收集器。
-
Parallel Compacting(Parallel Old)和 Parallel Scavenge 一起激活,并发收集。默认就是 ParallelGC。
-
CMS:并发标记清除垃圾回收器。
Concurrent Mark Sweep:是一种以获取最短回收停顿时间为目标的垃圾回收器。适合堆内存大,CPU核心数多的服务器应用。并发收集,低停顿,并发是指和用户线程一起执行。
Serial Old 的将作为CMS出错后的备用收集器。
- 初始标记:CMS initial mark:Stop the world,获取所有GC Roots对象,然后并发标记
- 并发标记:CMS concurrent mark:并发执行,主要标记过程,标记全部对象
- 重新标记:CMS remark:修正在并发标记过程中因为程序执行而导致标记变化的对象,需要Stop the world,仍需要暂停所有线程。二次确认。
- 并发清除:CMS concurrent sweep:清楚GC Roots不可达对象,和用户线程一起工作,不需要暂停工作线程。基于标记结果直接清理对象。GC耗时最多的是并发标记和并发清楚过程,所以可以减少GC时间。
优点:并发收集停顿低
缺点:由于并发执行,CMS在收集与应用线程会同时增加堆内存使用,所以要在堆内存耗尽前完成垃圾回收,否则CMS回收失败,会触发 Serial Old(MSC)以 STW 方法执行垃圾回收。标记清除算法无法整理内存碎片,所以会产生大量碎片空间,最后会通过担保机制对堆内存进行压缩。CMS也提供了 -XX:CMSFullGCsBeforeCompaction ,默认是0,每次都进行内存整理,指定几次后进行一个压缩的Full GC。
[root@centos02 java]# java -XX:CMSFullGCsBeforeCompaction=2 demo [1]+ Killed java -XX:ParallelGCThreads=4 demo Java HotSpot(TM) 64-Bit Server VM warning: CMSFullGCsBeforeCompaction is deprecated and will likely be removed in a future release. Hello world
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ec3sM3YG-1610585792185)(https://github.com/sunliancheng/image_Pub/blob/main/87AE05A0-5840-42A0-8445-D9522DEA71F6.png?raw=true)]
-
G1:
G1 是面向服务端的,更高的吞吐量和更小的暂停。G1和CMS相比是整理内存的,不会产生内存碎片,G1 的 STW更可控,可以添加预测机制,用户可以指定期望停顿时间。
对于G1来说,把内存划分成一个一个小区域块,避免整个内存扫描,每个 1m - 32m 不等,必须是2的次方,通过参数 -XX:G1HeapRegionSize=n 设置。最多把内存分为2048个块,最大64g。整体上使用标记-整理算法,局部是复制算法,不会产生内存碎片。G1 只有逻辑上的逻辑隔离。
G1仍然属于分代收集器。对于新生代的Region,仍然暂停所有应用线程,把存活的对象放入老年代或者是Survivor空间。部分region包含老年代,把对象从一个区域挪到另一个区域,完成了部分堆堆压缩,保证没有碎片化空间。
对于大对象,直接放入Region中,特殊区域(Humongous)
[root@centos02 java]# java -XX:+UseG1GC demo Hello world
使用G1后,垃圾回收日志会打印:heap 和 metaspace,会把堆分成两个部分(元空间逻辑上在堆中)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qkpm1X78-1610585792187)(https://github.com/sunliancheng/image_Pub/blob/main/FCF1A38C-51E5-454C-A116-04187246CE4B.png?raw=true)]
Server/Client模式
64位使用Server模式,32位系统和小内存少cpu会使用Client模式
[root@centos02 ~]# java -version
java version "1.8.0_261"
Java(TM) SE Runtime Environment (build 1.8.0_261-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, mixed mode)
Springboot 和 GC
启动 jar 包的时候,带有JVM参数启动。
java -server -Xms1024m -Xmx1024m -XX:+UseG1GC xxx.jar
Linux 操作和优化
top
top - 14:25:37 up 1 day, 1:38, 2 users, load average: 0.00, 0.02, 0.05
Tasks: 114 total, 1 running, 112 sleeping, 1 stopped, 0 zombie
%Cpu(s): 0.2 us, 0.2 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1863024 total, 119244 free, 1403604 used, 340176 buff/cache
KiB Swap: 2097148 total, 2096116 free, 1032 used. 303240 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1051 root 20 0 512128 35116 10160 S 0.7 1.9 64:42.77 /usr/bin/+
676 root 20 0 272976 4464 3324 S 0.3 0.2 12:07.67 /usr/bin/+
1060 root 20 0 144292 2016 932 S 0.3 0.1 13:20.23 /usr/loca+
1 root 20 0 43576 3724 2336 S 0.0 0.2 0:02.51 /usr/lib/+
2 root 20 0 0 0 0 S 0.0 0.0 0:00.08 [kthreadd]
4 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kworker/+
6 root 20 0 0 0 0 S 0.0 0.0 0:03.53 [ksoftirq+
7 root rt 0 0 0 0 S 0.0 0.0 0:00.08 [migratio+
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 [rcu_bh]
9 root 20 0 0 0 0 S 0.0 0.0 4:00.93
load average:表示 1 分钟,5分钟,15分钟负载的平均值
键盘中摁 1 ,会详细展示 cpu 核数情况:
%Cpu0 : 0.3 us, 0.3 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.3 us, 0.7 sy, 0.0 ni, 99.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
vmstat
查看cpu信息
[root@centos02 ~]# vmstat -n 2 3 //每两秒采样一次,共计三次
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 1032 108168 40 340168 0 0 3 1 149 231 1 1 98 0 0
0 0 1032 108348 40 340168 0 0 0 0 259 417 0 1 99 0 0
0 0 1032 108316 40 340168 0 0 0 0 277 422 0 1 99 0 0
r表示 runtime,运行和等待cpu时间片的进程数,原则是1核cpu的运行队列不要超过2,整个系统不要超过总核心数2倍,b表示 blocking。
查看所有cpu核信息:
mpstat -P ALL 2
free
free是查看内存的, 最好使用 free -m
[root@centos02 java]# free -m
total used free shared buff/cache available
Mem: 1819 1399 77 9 342 267
Swap: 2047 1 2046
df
查看硬盘信息:
[root@centos02 java]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 899M 0 899M 0% /dev
tmpfs 910M 0 910M 0% /dev/shm
tmpfs 910M 9.6M 901M 2% /run
tmpfs 910M 0 910M 0% /sys/fs/cgroup
/dev/mapper/centos-root 17G 9.6G 7.5G 57% /
/dev/sda1 1014M 194M 821M 20% /boot
tmpfs 182M 0 182M 0% /run/user/0
iostat
iostat -xdk 2 3
重点看 %util 每秒有多少时间用于 IO 操作
ifstat
[root@centos02 java]# ifstat
#kernel
Interface RX Pkts/Rate TX Pkts/Rate RX Data/Rate TX Data/Rate
RX Errs/Drop TX Errs/Drop RX Over/Rate TX Coll/Rate
lo 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
ens33 40485 0 22557 0 3414K 0 7033K 0
0 0 0 0 0 0 0 0
docker0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
du
查看文件占用大小
[root@centos02 java]# du -sh demo.class
4.0K demo.class4.0K demo.class
tail
cpu 占用过高思路
- TOP 找出 pid 进程占用cpu多多
- ps -ef 或者 jps 继续定位查出 pid 对应的
- ps -mp 进程号 -o THREAD,tid,time 查出具体的线程号
- 将需要的线程 ID转换为16进制 编号: printf “%x\n”
- jstack进程ID | grep tid -A60
举例子:一个while循环的java在跑
[root@centos02 java]# top
top - 14:51:34 up 1 day, 2:04, 2 users, load average: 0.64, 0.18, 0.09
Tasks: 118 total, 3 running, 110 sleeping, 5 stopped, 0 zombie
%Cpu(s): 17.6 us, 32.3 sy, 0.0 ni, 46.6 id, 0.2 wa, 0.0 hi, 3.3 si, 0.0 st
KiB Mem : 1863024 total, 100608 free, 1472504 used, 289912 buff/cache
KiB Swap: 2097148 total, 2096116 free, 1032 used. 234188 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
111644 root 20 0 2680020 30756 11788 S 85.3 1.7 0:09.55 java
99818 root 20 0 161724 6268 4880 R 18.7 0.3 0:02.60 sshd
111523 root 20 0 0 0 0 S 1.0 0.0 0:00.13 kworker/1+
1051 root 20 0 512128 33132 8188 S 0.7 1.8 65:07.64 containerd
99212 root 20 0 0 0 0 S 0.7 0.0 0:10.90 kworker/0+
[root@centos02 java]# jps -l
100099 demo
111693 sun.tools.jps.Jps
111644 t
[root@centos02 java]# ps -mp 111644 -o THREAD,tid,time //-m获取线程
USER %CPU PRI SCNT WCHAN USER SYSTEM TID TIME
root 84.2 - - - - - - 00:00:37
root 0.0 19 - futex_ - - 111644 00:00:00
root 83.4 19 - - - - 111645 00:00:37
root 0.0 19 - futex_ - - 111646 00:00:00
root 0.1 19 - futex_ - - 111647 00:00:00
root 0.2 19 - futex_ - - 111648 00:00:00
root 0.0 19 - futex_ - - 111649 00:00:00
root 0.0 19 - futex_ - - 111650 00:00:00
root 0.0 19 - futex_ - - 111651 00:00:00
root 0.2 19 - futex_ - - 111652 00:00:00
root 0.0 19 - futex_ - - 111653 00:00:00
root 0.0 19 - futex_ - - 111654 00:00:00
root 0.0 19 - futex_ - - 111655 00:00:00
[root@centos02 java]# printf "%x\n", 111645
1b41d
[root@centos02 java]# jstack 111644 | grep 1b41d -A60
"main" #1 prio=5 os_prio=0 tid=0x00007f5394009800 nid=0x1b41d runnable [0x00007f539c4b5000]
java.lang.Thread.State: RUNNABLE
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:326)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
- locked <0x00000000e383f140> (a java.io.BufferedOutputStream)
at java.io.PrintStream.write(PrintStream.java:482)
- locked <0x00000000e38030e0> (a java.io.PrintStream)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
- locked <0x00000000e3803100> (a java.io.OutputStreamWriter)
at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
at java.io.PrintStream.write(PrintStream.java:527)
- locked <0x00000000e38030e0> (a java.io.PrintStream)
at java.io.PrintStream.print(PrintStream.java:669)
at t.main(t.java:5)
"VM Thread" os_prio=0 tid=0x00007f5394073800 nid=0x1b420 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f539401e800 nid=0x1b41e runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f5394020800 nid=0x1b41f runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007f53940be000 nid=0x1b427 waiting on condition
JNI global references: 5
at t.main(t.java:5) 这句话就能看出是第几行代码有问题