前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试真题分享-线上多久一次FullGC?

面试真题分享-线上多久一次FullGC?

作者头像
可为编程
发布2024-06-24 14:35:06
860
发布2024-06-24 14:35:06
举报

今日题目

说说sql优化的经历

项目里有用过ConcurrentHashMap吗?底层结构有了解吗?

线上用的什么垃圾收集器?GC一次多久?线上多久一次Full GC?

内存泄露怎么分析?怎么知道整条内存泄露的链路?

算法:合并两个有序链表

1.内存泄漏怎么分析?

内存泄漏主要就是指程序占用内存不能及时释放内存,导致内存持续被占用从而内存泄漏。内存溢出是指程序占用内存过多,导致占用的内存满了无法满足新程序的内存分配使用从而导致内存溢出。内存泄漏会导致内存溢出。

关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!

内存泄漏会造成OOM,性能整体下降,系统分配额外内存影响整体系统的运行情况,程序运行延迟卡顿或者直接崩溃,因为可用内存少,频繁GC,频率高了,用户会感受到卡顿。这些现象都是内存泄漏的情况。

内存泄漏的分析和内存溢出的分析方法相同,内存溢出主要就是根据JVM命令找到对应的进程,然后根据进程号获取到内存dump文件,分析文件中是哪个方法和类占用过多造成内存溢出。

1、首先根据JVM指令JPS找到进程号

2、使用jmap生成dump文件

代码语言:javascript
复制
jmap -dump:live,format=b,file=D:/1.hprof 24956

3、然后导入到jvisualvm中或者是第三方软件中比如阿里的Arthas、JProfiler、VisualVM、MAT等。

代码语言:javascript
复制
jps:当前运行的所有java进程
jstat:单个java进程GC情况
jmap: 单个java进程中堆内存使用情况
jvisualvm:可视化查看堆内存与metaspace占用情况
jstack:查看具体某个java进行的线程堆栈情况
也可通过IDEA配置-verbose:gc

怎么知道整条内存泄露的链路?

内存泄漏应该关注fullGC的时间,看多久一次FullGC,将大的内存占用进程及时释放。释放内存的操作本身是由JVM帮我们来进行的,是自动的,也就是每次的FullGC将不需要的内存占用释放出来,当Java对象不再被引用时,理论上该对象就变得可以回收了。

程序员也可以来操作避免发生内存泄漏或者是发生了如何解决:

  • 1、及时释放无用的资源,当资源不用的时候及时关闭,比如数据库连接、文件流、网络连接等。
  • 2、当两个或多个对象之间存在循环引用时,就会导致它们都无法被释放,从而引起内存泄漏。
  • 3、大量的静态成员变量和集合,导致其生命周期会根据程序进程一致
  • 通过第三方软件或者是直接分析dump文件就可知道内存泄漏的链路。

2.用的什么垃圾收集器?GC一次多久?线上多久一次Ful GC

线上采用的是G1垃圾收集器,虽然默认的采用UseParallelGC这个垃圾回收器等于Parallel Scavenge + Parallel Old ,表示的是新⽣代⽤的 Parallel Scavenge 收集器,⽼年代⽤的是 Parallel Old 收集器。开创了局部收集的设计思路和基于多个相同区域Region的内存布局形式。G1解决了内存碎片和低停顿(STW)的问题,同时效率也不低。

GC一次的时间从几秒到几分钟一次时间不等,因为年轻代,老年代都有自己的GC回收器,每次时间根据情况不固定。线上FULLGC由几分钟到一天一次时间不固定,我们可以采用以下命令来检测我们程序的GC和FULLGC情况:

代码语言:javascript
复制
jstat -gc 24076 5000 10

假设Java进程的ID为24076,要每5秒采样一次,并采样10次。要查看线上Java应用程序的Full GC频率以及其他年龄代(如年轻代)的GC时间,可以通过以下步骤和工具进行操作:

1、使用jstat命令

jstat是JDK自带的一个轻量级工具,用于监视JVM的性能和资源使用情况。你可以使用它来查看GC相关的信息。

命令格式:

代码语言:javascript
复制
jstat -gc [vmid] [interval] [count]
vmid:Java虚拟机的进程ID。
interval:两次采样之间的时间间隔(以毫秒为单位)。
count:采样的次数。

输出将包括多个列,每列都代表一个不同的指标。其中与GC相关的指标包括:

S0C、S1C、EC:年轻代中Survivor区(两个)和Eden区的大小(KB)。

S0U、S1U、EU:年轻代中Survivor区(两个)和Eden区当前已使用的空间(KB)。

OC、OU:老年代的大小和当前已使用的空间(KB)。

YGC、YGCT:从应用程序启动到采样时年轻代中的GC次数和GC所用时间(秒)。

FGC、FGCT:从应用程序启动到采样时老年代(Full GC)的GC次数和GC所用时间(秒)。

关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!

2、计算Full GC的频率

如果你想要计算Full GC的频率,你需要知道Java进程已经运行了多长时间以及在这段时间内发生了多少次Full GC。

查看Java进程的运行时间:可以使用ps命令结合grep和awk等工具来获取。

计算Full GC频率:使用上述jstat命令获取到的FGC(Full GC次数)和进程的运行时间,然后计算Full GC次数 / 运行时间来得到Full GC的频率。

3、监控和告警

在实际的生产环境中,你可能希望设置监控和告警系统来自动检测Full GC的频率和其他性能问题。这可以通过将jstat的输出与一些脚本或监控工具(如Prometheus、Grafana、Zabbix等)集成来实现。

4. 其他注意事项

GC日志:除了使用jstat命令外,你还可以通过启用GC日志(通过JVM参数-XX:+PrintGCDetails -Xloggc:gc.log)来获取更详细的GC信息。然后,你可以使用如Eclipse Memory Analyzer (MAT)或VisualVM或GCViewer等工具来分析这些日志。

关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!

GC调优:如果发现Full GC过于频繁或耗时过长,可能需要考虑调整JVM的GC配置参数或优化应用程序的代码。这包括调整堆大小、选择更合适的GC算法、优化对象的生命周期管理等。

关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!

Full GC通常是较慢的,如果它过于频繁(如几十分钟就会执行一次),则可能导致工作线程频繁被停止,系统出现卡顿现象,程序的整体性能变差。

Full GC耗时过长(如几秒)也可能导致服务超时问题,特别是对于高并发服务。所以说你们觉得线上多久一次FullGC会更好呢?我下载下来gc.log文件之后没有发现FullGC的回收数据?难道没有达到一定量级的程序不会发生FullGC?欢迎感兴趣的小伙伴留言评论!

分析gc日志工具网上找到了这篇文章: https://www.cnblogs.com/yueluoshuxiang/p/11734313.html

关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!

3.项目里有用过ConcurrentHashMap吗?底层结构有了解吗?

有用过,他提供了多线程并发环境下对集合操作的安全性,是线程安全的集合类。因为HashMap本身是非线程安全的,比如一个线程在位置1处存入了值A,另外一个线程也在位置1处存放了值B,因此B就将A覆盖掉了,导致在读数据的时候发现数据不准确。同时多线程做Hash时也会导致Hash到同一位置,使得get获取数据的时候出现循环。

因此就要采用多线程环境下线程安全的集合类,CurrentHashMap就很好解决了这个问题。

Java1.7版本CurrentHashMap:

CurrentHashMap在Java1.7版本采用Segement分段加锁和Reentrantlock来保障线程安全性。锁定的是 Segment,锁的范围要更大,因此性能上会更低,内部采用Segement<K,V>[]数组,多线程操作的时候每个线程给Segement加锁,默认长度为16个Segement,因此支持16个并发同时操作。Segement内部采用了HashEntry<K,V>[]数组,HashEntry也是类似于HashMap的结构,保存Key-Value的键值对,同时还有下一个指针next,HashEntry之间通过Next指针进行关联。如果16个数据都满了或者是每一个Segement中的HashEntry数据达到了8个,就会触发扩容,在保障之前数据位置的前提下,扩容2的倍数,这里也和负载因子有关,8*0.75=6,所以从0开始到7的时候开始扩容,扩容后位置需要加上原数组长度。同时新增Put的时候采用头插法,同时增加获取锁和解锁的逻辑来保障线程安全性,比如线程1操作正在添加值,线程2也获取到这个Segement,也要往同一位置修改或者增加值,这样就得等线程1释放锁之后才能操作。此刻线程2会去计算自己要往哪个HashEntry数组位置放数组,会额外进行计算,如果线程1更改了数据的存放位置,线程2如果是修改的话就要重新进行Hash计算确定位置。

Java1.8版本CurrentHashMap:

1.8版本就有很大的不同,1.8版本的HashMap也有很大的改进,CurrentHashMap本质上底层也采用了HashMap的底层结构,通过Node数组+链表+红黑树+CAS+Synchronized来解决多线程并发安全性问题。底层与HashMap一样,只不过在加锁上采用了Snchronized关键字来实现,1.8在Synchronized上做了很多的优化,比如锁升级等。又是JVM关键字,用起来方便轻量,同时加锁的粒度也从原来的Segement转变成了数组的Node节点,锁的粒度降低。在新增put数据的时候采用CAS来判断,不再采用加锁方式,通过CAS来控制一个名为sizeCtl的变量,该变量默认为0,当一个线程初始化的时候会通过CAS方法将其设置为-1,由于其被volatile关键字修饰,保障了多线程内存之间的可见性。因此其他线程再操作的时候就会先拿到这个变量值进行CAS,判断当前内存地址的值与预期值0是否相同,不同就让出执行权,相同就将其改为新值也就是-1。其他线程也会根据其值判断其是否小于0,小于0直接让出执行权。同时还支持并发扩容的操作。当数组长度不够时,ConcurrentHashMap 需要对数组进行扩容,在扩容的实现上,ConcurrentHashMap 引入了多线程并发扩容的机制,简单来说就是多个线程对原始数组进行分片后,每个线程负责一个分片的数据迁移,从而提升了扩容过程中数据迁移的效率。

4.SQL优化的经历

SQL优化主要是两个方面,一个是对SQL结构语法层面的优化,第二个就是索引层面的优化,优化的本意初衷就是提升查询速度,避免慢SQL发生。

从SQL结构语法层面来说主要有以下几点:

1、查询时禁止使用select * ,这样效率低,而且会发生回表操作,不会走索引覆盖。

2、减少使用or连接操作,这样会造成索引失效。

3、能用not exist就不要用not in ,not in 会导致索引失效并且不能包含null值。

4、如果一个表是大表,一个表是小表,子查询表小用in,表大用exists。

5、建表时存储精度比较高的内容用DECIMAL或者NUMERIC类型值作为字符串存储。

6、删除数据操作drop>truncate>delete,但是要确保是否要保留表结构。

7、union会清除重复的数据,union ALL会保留重复的数据但是效率会快很多。

8、count(主键)>count(1)>count(非主键列名) >count(*)。

9、如果有过滤条件和分组同时存在时,可以先筛选之后再分组,减少数据量。

10、尽量不要超过三个表相互关联,需要join的字段,数据类型保持绝对一致。多表关联查询时,保证被关联的字段需要有索引。inner join速度最快。

11、若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。

12、查询运算符、like、between and、in、not in对NULL值查询不起效。

从索引层面来说有以下几点:

1、用explain关键字检查sql语句是否走索引,对查询使用比较频繁的字段且区分度高的字段增加索引。

2、尽量少建立单个索引,建立联合索引减少对索引的维护,同时在5.6之后的版本在联合索引存在的情况下,查询语句中的where条件都是索引时还会触发索引下推,减少回表操作,但只可用于非聚簇索引的列。

3、尽量减少使用索引失效的SQL写法,以下均会造成索引失效:

3.1、减少使用or连接操作,这样会造成索引失效

3.2、like的前缀匹配%like或者%like%会导致索引失效,因为如果所有数据都包含指定关键字,那么匹配出来的就是全部数据,只能通过io的方式加载所有叶子节点数据,并且遍历所有记录进行过滤,才可以找到包含关键字的记录。

3.3、把最频繁使用的查询关键字放在联合索引的最左边,避免找不到索引造成索引失效,因为B+树的叶子节点是按照从左到右由小到大排列的,因此先将最左边的索引由大到小排列,命中最左边的索引后,确定下一步检索的方向,再将数据按照下一个索引进行从左到右排序。

3.4、范围查询后面还有条件会导致后面的条件索引失效。类似这种的还有>、<、between and,多字段索引的情况下,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配。比如a=1 and b>=0 and c=1 这种情况只能先确定a=1 and b>=0所在页的范围,然后对这个范围的所有页进行遍历,c字段在这个查询的过程中,是无法确定c的数据在哪些页的,此时我们称c是不走索引的,只有a、b能够有效的确定索引页的范围。

3.5、当多个条件中有索引的时候,并且关系是and的时候,会走索引区分度高的

3.6、如果列的重复值越多,离散度就越低,重复值越少,离散度就越高,不建议大家在离散度低的字段上建立索引。MySQL会将其识别为与全表扫描速度相差不大,就算建立了索引也不一定会走。

3.7、字符串类型的索引不能和数值类型的索引相互比较,这样会造成全表扫描导致索引失效,同时字符串类型会转变为数值类型,造成全表扫描。比如name为字符串索引设置值‘1’与 1进行比较会导致索引失效。但是id如果为数值类型的索引‘1’与1 查询效果一样。因此尽量采用符合字段定义的类型进行比较,相同的类型进行比较。

3.8、索引字段使用函数查询时索引无效。当使用函数时无法快速查询到数据所在的页,只能将所有页的记录加载到内存中,然后对每条数据使用函数进行计算之后再进行条件判断,此时数据无效,变成了全表数据扫描。

3.9、对索引字段进行运算符计算操作,导致全表扫描。

关注公众号【可为编程】回复【加群】进入面试技术交流群!!!

算法-两个有序链表相加

迭代法:

代码语言:javascript
复制
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
    if (list1 == null) {
        return list2;
    }
    if (list2 == null) {
        return list1;
    }
    ListNode result = new ListNode();
    ListNode p = result;
    while (list1 != null && list2 != null) {
        if (list1.val < list2.val) {
            p.next = list1;
            list1 = list1.next;
        } else {
            p.next = list2;
            list2 = list2.next;
        }
        p = p.next;
    }
    p.next = list1 == null ? list2 : list1;
    return result.next;
}

大家还有哪些解法可以在底下评论区打出,或者有对其他题目的见解欢迎留言评论,一起探讨一起精进。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 可为编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档