sonar 检测 Call “Optional#isPresent()“ before accessing the value.(java 8 版本)

本文介绍了如何在Java8中正确使用Optional以避免NPE,尤其是在Stream操作中。讨论了两种修正策略:一是中断链式调用进行空检查,二是利用orElseGet实现默认值。还提到针对特定场景的优化,如仅处理属性值并使用mapToLong。

问题代码

public class demoUtil {
	public static Long methdName(List<EnterpriseSolutionsSeq> seqList){
		if(CollUtil.isEmpty(seqList)){
			return null;
		}
		// 下面这行代码会被sonar检测有问题,因为在对Optional对象使用get前没有使用 isPresent()进行校验
		// Optional对象来源因为用了Stream 的 min 方法,其返回值是 Optional类型的
		EnterpriseSolutionsSeq solutionsSeq = seqList.stream().filter(Objects::nonNull).min(Comparator.comparing(EnterpriseSolutionsSeq::getSeq)).get();
		return solutionsSeq.getSeq();
	}
}

解决问题的背景知识

java8 Optional 的使用规范要求的规则。

推荐阅读:理解、学习与使用 Java 中的 Optional

个人读后感:Optional 是为了解决 java 经常遇到的 NullPointerException(简称NPE) 异常而引入的新的类。Optional只是一个语法糖,就算使用了 Optional 但如果不规范使用还是会有NPE出现。感觉有两点作用,一点是当编码时遇到传过来的是Optional类型时,警醒开发人员需要关注NPE风险(这点对于sonar等代码检测工具有很大的帮助);另外一点价值更大的就是对于链式调用和lambda的使用方式时,对于判定对象是否为null,安排后续处理逻辑,可以链式到底(java9也完善了下Optional的链式方法)。

Optional 使用规范使用get方法(获取对象)前要先使用 isPresent() 来判定Optiona包含的对象是否为空。

对于这点跟使用普通的 if else 判定对象是否为 null没有什么区别,只不过语法检测工具可以根据当前是Optional对象来更好的明确提醒你判定是否为null

推荐阅读:Optional用法(Java SE 8–Java SE 9)

Java 8 Stream

推荐阅读:Java 8 Stream

本次问题产生原因就是 Stream 一些方法会返回 Optional 类型数据,直接使用get()导致的。

修改策略

java8

方法1

中断了链式编程,拿出min处理后的 Optional 结果 来if isPresent() 判定下,这是本人最开始百度找到的解决sonar报错的简单粗暴解决方案,不推荐。

public class demoUtil {

	public static Long methdName(List<EnterpriseSolutionsSeq> seqList){
		if(CollUtil.isEmpty(seqList)){
			return null;
		}
		
		Optional<EnterpriseToolsSeq> min = toolsSeqList.stream().filter(Objects::nonNull).min(Comparator.comparing(EnterpriseToolsSeq::getSeq));
		if(min.isPresent()){
			return min.get().getSeq();
		}
		return 1000000L;
	}
}
方式2

使用orElseGet 或者 orElse。

推荐阅读:Java8之Optional中orElse和orElseGet的区别

推荐使用 orElseGet(null时候被调用的方法,一般是封装一个会返回默认值的对象方法),除非Null时返回的对象已经存在创建好,否则不推荐使用orElse(null时返回的默认对象或方法执行后返回的默认对象)。
orElseGet该方法逻辑是,判定对象如果不是null 则直接返回该对象,如果是null则调用OrElseGet后面括号里的方法,一般是组装一个默认对象并返回的方法。
OrElse该方法逻辑是,判定对象如果不是null 则直接返回该对象,如果是null则触发返回OrElse后面括号里的默认对象,或者是能返回对象的方法。
没有使用方便的 orElse 是因为对性能影响风险小,本例来说orElse 和orElseGet性能影响不大。

参考阅读:Java 9 改进的 Optional 类添加了三个方法来改进它的功能:stream(),ifPresentOrElse(),or()

public class demoUtil {

	public static Long methdName(List<EnterpriseSolutionsSeq> seqList){
		if(CollUtil.isEmpty(seqList)){
			return null;
		}
		EnterpriseSolutionsSeq solutionsSeq = seqList.stream().filter(Objects::nonNull).min(Comparator.comparing(EnterpriseSolutionsSeq::getSeq)).orElseGet( () -> {
			EnterpriseSolutionsSeq enterpriseSolutionsSeq = new EnterpriseSolutionsSeq();
			enterpriseSolutionsSeq.setSeq(100000L);
			return  enterpriseSolutionsSeq;
		});
		return solutionsSeq.getSeq();
	}
}

鉴于本次返回只需要比较对象里的一个属性(Long类型),可以在Stream期间只处理改属性的值,用map(本次是MapToLong)方法处理下。再比较大小,以及最后返回默认的10000L

public class demoUtil {

	public static Long methdName(List<EnterpriseSolutionsSeq> seqList){
		if(CollUtil.isEmpty(seqList)){
			return null;
		}
		return toolsSeqList.stream().filter(Objects::nonNull).mapToLong(en->en.getSeq()).min().orElseGet(()->{return 100000L;});
	}
}
<think> 我们正在处理用户的问题:在SonarQube配置了数据库、Web服务和登录信息后出现内存错误。 根据用户描述,配置包括: - 数据库配置:sonar.jdbc.url, sonar.jdbc.username, sonar.jdbc.password - Web服务配置:sonar.host.url - 登录配置:sonar.login, sonar.password 注意:用户提到“内存错误”,结合之前的历史问题(Elasticsearch退出78)以及当前配置,我们推断可能是由于配置增加后,系统资源(尤其是内存)不足导致的。 解决方案思路: 1. 检查日志:确定内存错误的具体表现(是OOM还是其他内存不足错误) 2. 调整JVM内存设置:增加SonarQube应用服务器和Elasticsearch的内存 3. 优化配置:检查是否有配置错误导致内存占用过高 4. 考虑使用外部数据库(如MySQL)和外部Elasticsearch集群来减轻嵌入式ES的压力 具体步骤: ### 1. 确认错误日志 - 查看SonarQube日志(位于`<SONARQUBE_HOME>/logs/sonar.log`),搜索`OutOfMemoryError`或`java.lang.OutOfMemoryError`。 - 检查Elasticsearch日志(`es.log`)是否有类似错误。 ### 2. 调整JVM内存设置 - SonarQube应用服务器内存调整:修改`<SONARQUBE_HOME>/conf/wrapper.conf`(旧版)或`<SONARQUBE_HOME>/conf/sonar.properties`(新版)中的JVM参数。 - 在`sonar.properties`中,找到或添加: ```properties sonar.web.javaOpts=-Xmx2g -Xms1g -XX:+HeapDumpOnOutOfMemoryError ``` 说明:`-Xmx2g`设置最大堆内存为2GB,`-Xms1g`设置初始堆内存为1GB。根据机器配置调整(例如,如果机器有8G内存,可以设置为`-Xmx4g`)。 - Elasticsearch内存调整(如果使用嵌入式ES): ```properties sonar.search.javaOpts=-Xmx2g -Xms1g -XX:MaxDirectMemorySize=256m ``` 注意:嵌入式ES的堆内存不要超过物理内存的50%,且建议小于32GB(因为超过32GB会降低性能)。 ### 3. 检查配置是否正确 - 确保数据库配置正确(连接字符串、用户名、密码),错误的配置可能导致连接泄漏或重试风暴,从而内存耗尽。 - 参考用户引用的配置示例[^1]: ```properties sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance ``` 注意:如果使用MySQL,需要确保MySQL服务已启动,且SonarQube有权限访问。 - 检查`sonar.host.url`是否正确,如: ```properties sonar.host.url=http://localhost:9000 ``` - 登录配置(`sonar.login`和`sonar.password`)通常用于生成token,如果是在配置文件中直接写明文密码,注意安全。但通常不会直接导致内存错误。 ### 4. 排除不必要的分析(减少内存占用) - 在项目配置中(`sonar-project.properties`),排除不需要分析的代码目录[^2]: ```properties sonar.exclusions=**/test/**, **/generated/**, **/node_modules/** ``` 减少分析范围可以降低内存使用。 ### 5. 升级硬件或使用外部服务 - 如果机器内存不足,考虑升级内存。 - 使用外部Elasticsearch集群:将SonarQube的嵌入式Elasticsearch替换为独立的Elasticsearch集群,这样可以分散内存压力(参考问题1的解决方案)。 - 使用外部数据库:确保数据库服务独立部署,避免和SonarQube应用争抢资源。 ### 6. 其他优化 - 重启SonarQube:在调整配置后,重启服务使配置生效。 - 监控内存使用:使用工具(如top, htop, jstat)监控JVM内存使用情况。 验证: - 重启后访问SonarQube,查看是否正常。 - 检查日志文件是否有新的错误。 注意:如果问题仍然存在,请提供具体的错误日志片段以便进一步分析。 根据上述步骤,用户应能解决内存错误问题。 相关问题: 1. 如何监控SonarQube的内存使用情况? 2. SonarQube如何配置外部Elasticsearch集群? 3. SonarQube如何优化数据库连接性能? </think> 针对您在SonarQube配置数据库、Web服务和登录信息后出现内存错误的问题,以下是系统性解决方案: --- ### 📌 核心问题定位 配置数据库、Web服务和登录后出现内存错误,通常由以下原因导致: 1. **内存资源不足**:新增服务消耗更多资源,超出默认JVM堆配置 2. **数据库连接泄漏**:不当的JDBC配置导致连接未释放[^1] 3. **并发压力增大**:Web服务暴露后访问量增加 4. **配置冲突**:多模块配置叠加引发资源争用 --- ### 🔧 解决方案步骤 #### 1. 调整JVM内存配置 修改`<SONARQUBE_HOME>/conf/sonar.properties`: ```properties # 应用服务器内存 (推荐物理内存30%-50%) sonar.web.javaOpts=-Xmx4g -Xms2g -XX:+HeapDumpOnOutOfMemoryError # Elasticsearch内存 (需小于32GB) sonar.search.javaOpts=-Xmx8g -Xms4g -XX:MaxDirectMemorySize=2g ``` > 💡 **关键点**: > - `sonar.web`内存 ≤ 物理内存50% > - `sonar.search`内存 ≤ 物理内存70% > - 添加`-XX:+HeapDumpOnOutOfMemoryError`生成内存快照 #### 2. 优化数据库连接池 在MySQL配置后添加连接池参数[^1][^3]: ```properties sonar.jdbc.url=jdbc:mysql://localhost:3306/sonar?useUnicode=true&characterEncoding=utf8 &rewriteBatchedStatements=true&useConfigs=maxPerformance &maxPoolSize=20 # 限制最大连接数 &connectionTimeout=30000 # 30秒超时 ``` > ✅ **验证命令**: > `SHOW STATUS LIKE 'Threads_connected';` 监控MySQL活跃连接数 #### 3. 排除非必要资源分析 在`sonar-project.properties`中添加排除规则[^2]: ```properties sonar.exclusions=**/test/**, **/node_modules/**, **/vendor/** sonar.global.exclusions=**/*.min.js, **/*.bak ``` #### 4. Web服务限流配置 添加流量控制参数: ```properties # 每个IP每秒最大请求数 sonar.web.http.socketBacklog=500 sonar.web.http.maxThreads=100 ``` #### 5. 登录安全加固 避免明文密码,改用Token认证: ```bash # 生成管理员Token curl -u admin:原密码 -X POST "http://localhost:9000/api/user_tokens/generate?name=cli_token" ``` 在配置中替换为: ```properties sonar.login=生成的Token值 ``` --- ### 紧急恢复措施 若已出现OOM崩溃: 1. 查找内存快照:`<SONARQUBE_HOME>/logs/java_pid<数字>.hprof` 2. 使用MAT工具分析内存泄漏点: ```bash java -jar mat/ParseHeapDump.sh java_pid12345.hprof ``` 3. 清理临时文件: ```bash rm -rf <SONARQUBE_HOME>/temp/* ``` --- ### 📊 内存监控建议 | 监控指标 | 推荐工具 | 安全阈值 | |---------------------------|------------------------|----------------| | JVM堆内存使用率 | VisualVM/JConsole | ≤80% | | MySQL连接数 | SHOW PROCESSLIST | ≤ maxPoolSize | | ES的JVMMemory压力 | Elasticsearch Head | ≤75% | | 文件描述符使用量 | lsof -p <PID> | ≤90% | --- ### ✅ 验证流程 1. 增量重启服务: ```bash ./bin/linux-x86-64/sonar.sh start tail -f logs/web.log -n 500 ``` 2. 压测验证: ```bash ab -n 1000 -c 50 http://localhost:9000/api/system/status ``` > 📌 **最佳实践**:生产环境建议将MySQL和Elasticsearch部署在独立服务器[^3]。 --- ### 💎 延展优化 1. 启用G1垃圾回收器: ```properties sonar.web.javaOpts=... -XX:+UseG1GC -XX:G1HeapRegionSize=16m ``` 2. 配置SWAP交换空间(Linux): ```bash sudo fallocate -l 4G /swapfile && sudo chmod 600 /swapfile sudo mkswap /swapfile && sudo swapon /swapfile ``` 通过上述配置调整,可解决95%以上的配置性内存错误。若问题持续,请提供`logs/es.log`中的OOM错误片段进一步诊断。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值