当前位置: 首页 > article >正文

Java垃圾回收机制详解:从原理到实践

Java垃圾回收机制详解:从原理到实践

前言

垃圾回收(Garbage Collection,简称GC)是Java虚拟机自动管理内存的核心机制之一。它负责自动识别和回收不再被程序使用的内存空间,从而避免内存泄漏和溢出问题。深入理解垃圾回收机制对于Java开发者来说至关重要,它不仅能帮助我们写出更高效的代码,还能在遇到性能问题时进行有效的调优。

1. 垃圾回收基础概念

1.1 什么是垃圾

在Java中,"垃圾"指的是不再被任何对象引用的内存空间。当一个对象失去所有引用时,它就成为了垃圾,等待被垃圾回收器回收。

public class GCDemo {public static void main(String[] args) {// 创建对象String str = new String("Hello World");// 对象失去引用,成为垃圾str = null;// 此时原来的"Hello World"字符串对象成为垃圾System.gc(); // 建议进行垃圾回收(仅是建议)}
}

1.2 引用类型

Java中有四种引用类型,它们对垃圾回收的影响各不相同:

强引用(Strong Reference)
  • 默认的引用类型
  • 只要强引用存在,垃圾回收器永远不会回收被引用的对象
Object obj = new Object(); // 强引用
软引用(Soft Reference)
  • 内存空间足够时不会被回收
  • 内存空间不足时会被回收
SoftReference<Object> softRef = new SoftReference<>(new Object());
弱引用(Weak Reference)
  • 只要垃圾回收器运行,就会被回收
WeakReference<Object> weakRef = new WeakReference<>(new Object());
虚引用(Phantom Reference)
  • 无法通过虚引用获得对象实例
  • 主要用于跟踪对象被垃圾回收的状态
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), new ReferenceQueue<>());

2. 内存区域与对象分配

2.1 堆内存结构

Java堆内存采用分代收集策略,主要分为以下区域:

堆内存
├── 年轻代(Young Generation)
│   ├── Eden区
│   ├── Survivor 0区(S0)
│   └── Survivor 1区(S1)
└── 老年代(Old Generation)

2.2 对象分配流程

public class ObjectAllocationDemo {public static void main(String[] args) {// 1. 新创建的对象首先分配到Eden区List<String> list = new ArrayList<>();// 2. 不断添加对象for (int i = 0; i < 1000000; i++) {list.add("Object" + i);// 当Eden区满时,触发Minor GC// 存活对象移到Survivor区// 经过多次GC后,长期存活的对象进入老年代}}
}

3. 垃圾回收算法

3.1 标记-清除算法(Mark-Sweep)

原理:

  1. 标记阶段:遍历所有可达对象,进行标记
  2. 清除阶段:回收未被标记的对象

优缺点:

  • 优点:实现简单
  • 缺点:产生内存碎片,效率不高
// 伪代码示例
public class MarkSweepGC {public void markSweep() {// 标记阶段markReachableObjects();// 清除阶段sweepUnmarkedObjects();}private void markReachableObjects() {// 从GC Roots开始,标记所有可达对象}private void sweepUnmarkedObjects() {// 回收所有未标记的对象}
}

3.2 复制算法(Copying)

原理:
将内存分为两个相等的区域,每次只使用其中一个。垃圾回收时,将存活对象复制到另一个区域。

适用场景:

  • 年轻代垃圾回收
  • 存活对象较少的场景
public class CopyingGCDemo {private Object[] fromSpace;private Object[] toSpace;private int fromPointer = 0;private int toPointer = 0;public void copyingGC() {// 将存活对象从fromSpace复制到toSpacecopyLiveObjects();// 交换空间swapSpaces();// 清空原fromSpaceclearFromSpace();}
}

3.3 标记-整理算法(Mark-Compact)

原理:

  1. 标记阶段:标记存活对象
  2. 整理阶段:将存活对象向内存一端移动

优缺点:

  • 优点:没有内存碎片
  • 缺点:整理过程需要移动对象,效率较低
public class MarkCompactGC {public void markCompact() {// 标记存活对象markLiveObjects();// 整理内存,消除碎片compactMemory();}
}

3.4 分代收集算法

核心思想:

  • 年轻代:使用复制算法
  • 老年代:使用标记-清除或标记-整理算法
public class GenerationalGCDemo {public void youngGenerationGC() {// Eden区 + Survivor区 -> Survivor区copyFromEdenAndSurvivorToSurvivor();// 年龄增长,达到阈值进入老年代promoteToOldGeneration();}public void oldGenerationGC() {// 使用标记-整理算法markCompactOldGeneration();}
}

4. 垃圾收集器详解

4.1 Serial收集器

特点:

  • 单线程收集器
  • 收集时暂停所有工作线程(Stop The World)
  • 适用于客户端模式

启用参数:

-XX:+UseSerialGC

4.2 ParNew收集器

特点:

  • Serial收集器的多线程版本
  • 年轻代并行收集器
  • 与CMS收集器配合使用

启用参数:

-XX:+UseParNewGC

4.3 Parallel Scavenge收集器

特点:

  • 关注吞吐量
  • 支持自适应调节策略
  • 年轻代并行收集器

启用参数:

-XX:+UseParallelGC
-XX:MaxGCPauseMillis=200  # 最大停顿时间
-XX:GCTimeRatio=99        # 吞吐量大小

4.4 CMS收集器(Concurrent Mark Sweep)

特点:

  • 并发收集器
  • 低停顿时间
  • 老年代收集器

收集过程:

  1. 初始标记(STW)
  2. 并发标记
  3. 重新标记(STW)
  4. 并发清除

启用参数:

-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=70  # 触发CMS收集的堆使用率

示例配置:

// JVM启动参数示例
// -Xms2g -Xmx2g -XX:+UseConcMarkSweepGC 
// -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=70

4.5 G1收集器(Garbage First)

特点:

  • 面向服务端应用
  • 低延迟
  • 可预测的停顿时间

内存布局:

G1堆内存布局
├── Region 1 (Eden)
├── Region 2 (Survivor)
├── Region 3 (Old)
├── Region 4 (Humongous) # 大对象区域
└── ...

启用参数:

-XX:+UseG1GC
-XX:MaxGCPauseMillis=200     # 期望的最大停顿时间
-XX:G1HeapRegionSize=16m     # Region大小

G1收集过程示例:

public class G1GCDemo {public static void main(String[] args) {// 配置G1参数// -XX:+UseG1GC -XX:MaxGCPauseMillis=100List<byte[]> list = new ArrayList<>();// 模拟内存分配for (int i = 0; i < 1000; i++) {// 分配不同大小的对象byte[] array = new byte[1024 * 1024]; // 1MBlist.add(array);if (i % 100 == 0) {System.out.println("已分配 " + (i + 1) + " 个1MB对象");}}}
}

4.6 ZGC和Shenandoah收集器

ZGC特点:

  • 超低延迟收集器
  • 停顿时间不超过10ms
  • 支持TB级别堆内存

启用参数:

-XX:+UnlockExperimentalVMOptions -XX:+UseZGC

5. GC调优实践

5.1 GC监控和分析

常用工具:

  1. jstat - 监控GC统计信息
  2. jmap - 生成堆转储
  3. jvisualvm - 可视化监控工具
  4. GCViewer - GC日志分析工具

监控脚本示例:

#!/bin/bash
# GC监控脚本# 获取Java进程PID
PID=$(jps | grep "YourApp" | awk '{print $1}')# 监控GC情况
echo "=== GC监控开始 ==="
jstat -gc $PID 5s

5.2 GC日志配置

JDK 8及以前版本:

-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime
-Xloggc:gc.log

JDK 9及以后版本:

-Xlog:gc:gc.log:time,level,tags

5.3 调优策略

内存分配调优
public class MemoryTuningDemo {public static void main(String[] args) {// 预分配集合容量,减少扩容带来的GC压力List<String> list = new ArrayList<>(10000);// 使用对象池技术,减少对象创建ObjectPool<StringBuilder> pool = new ObjectPool<StringBuilder>() {@Overrideprotected StringBuilder create() {return new StringBuilder();}@Overrideprotected void reset(StringBuilder obj) {obj.setLength(0);}};StringBuilder sb = pool.acquire();try {sb.append("Hello World");// 使用StringBuilder} finally {pool.release(sb);}}
}// 简单的对象池实现
abstract class ObjectPool<T> {private final Queue<T> pool = new ConcurrentLinkedQueue<>();public T acquire() {T object = pool.poll();return object != null ? object : create();}public void release(T object) {reset(object);pool.offer(object);}protected abstract T create();protected abstract void reset(T object);
}
参数调优示例
# 生产环境G1调优参数示例
java -Xms4g -Xmx4g \-XX:+UseG1GC \-XX:MaxGCPauseMillis=100 \-XX:G1HeapRegionSize=16m \-XX:G1NewSizePercent=30 \-XX:G1MaxNewSizePercent=40 \-XX:G1MixedGCCountTarget=8 \-XX:InitiatingHeapOccupancyPercent=45 \-XX:+G1PrintRegionRememberedSetInfo \-Xlog:gc:gc.log:time,level,tags \YourApplication

5.4 常见问题与解决方案

问题1:Full GC频繁

可能原因:

  • 老年代空间不足
  • 元数据区空间不足
  • 内存泄漏

解决方案:

// 1. 增加堆内存
-Xms8g -Xmx8g// 2. 调整年轻代比例
-XX:NewRatio=2  // 老年代:年轻代 = 2:1// 3. 增加元数据区大小
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
问题2:GC停顿时间过长

解决方案:

// 使用G1收集器,设置期望停顿时间
-XX:+UseG1GC -XX:MaxGCPauseMillis=100// 或者使用ZGC(JDK 11+)
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
问题3:内存泄漏检测
public class MemoryLeakDemo {private static final Map<String, Object> cache = new HashMap<>();public static void main(String[] args) {// 模拟内存泄漏 - 对象不断添加到静态集合中Timer timer = new Timer();timer.scheduleAtFixedRate(new TimerTask() {private int counter = 0;@Overridepublic void run() {// 不断添加对象,但从不清理cache.put("key" + counter++, new byte[1024 * 1024]);if (counter % 100 == 0) {System.out.println("Cache size: " + cache.size());System.out.println("Free memory: " + Runtime.getRuntime().freeMemory() / 1024 / 1024 + " MB");}}}, 0, 100);}
}

检测方法:

# 生成堆转储
jmap -dump:format=b,file=heap.hprof <pid># 使用MAT或jvisualvm分析堆转储文件

6. 实际应用案例

6.1 高并发Web应用调优

场景:

  • 日PV 1000万+
  • 并发用户数10000+
  • 99%请求响应时间 < 100ms

调优方案:

# JVM参数配置
-Xms8g -Xmx8g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=50
-XX:G1HeapRegionSize=16m
-XX:G1NewSizePercent=40
-XX:G1MaxNewSizePercent=50
-XX:InitiatingHeapOccupancyPercent=35
-XX:+UnlockExperimentalVMOptions
-XX:G1MixedGCLiveThresholdPercent=85

应用代码优化:

@Component
public class CacheOptimizedService {// 使用Caffeine本地缓存,减少GC压力private final Cache<String, Object> localCache = Caffeine.newBuilder().maximumSize(10000).expireAfterWrite(Duration.ofMinutes(10)).build();// 对象池化,减少频繁创建销毁private final ObjectPool<StringBuilder> stringBuilderPool = new GenericObjectPool<>(new StringBuilderFactory());public String processData(String input) {// 先从缓存获取Object cached = localCache.getIfPresent(input);if (cached != null) {return (String) cached;}// 使用对象池StringBuilder sb = null;try {sb = stringBuilderPool.borrowObject();sb.append("Processed: ").append(input);String result = sb.toString();localCache.put(input, result);return result;} catch (Exception e) {throw new RuntimeException(e);} finally {if (sb != null) {try {stringBuilderPool.returnObject(sb);} catch (Exception e) {// 忽略归还异常}}}}
}

6.2 大数据处理应用调优

场景:

  • 单次处理数据量:1TB+
  • 内存使用:32GB+
  • 要求:低延迟,高吞吐

调优方案:

# 大堆内存配置
-Xms32g -Xmx32g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=32m
-XX:ParallelGCThreads=16
-XX:ConcGCThreads=8
-XX:+UnlockExperimentalVMOptions
-XX:+UseTransparentHugePages

数据处理优化:

public class BigDataProcessor {// 使用NIO和内存映射文件,减少GC压力public void processLargeFile(String filePath) throws IOException {try (RandomAccessFile file = new RandomAccessFile(filePath, "r");FileChannel channel = file.getChannel()) {long fileSize = channel.size();long chunkSize = 1024 * 1024 * 1024; // 1GB chunksfor (long position = 0; position < fileSize; position += chunkSize) {long size = Math.min(chunkSize, fileSize - position);// 使用内存映射,减少对象创建MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, position, size);processChunk(buffer);// 强制释放映射unmapBuffer(buffer);}}}private void processChunk(ByteBuffer buffer) {// 流式处理,避免一次性加载大量对象到内存while (buffer.hasRemaining()) {// 处理数据...}}private void unmapBuffer(MappedByteBuffer buffer) {try {Method cleanerMethod = buffer.getClass().getMethod("cleaner");cleanerMethod.setAccessible(true);Object cleaner = cleanerMethod.invoke(buffer);if (cleaner != null) {Method cleanMethod = cleaner.getClass().getMethod("clean");cleanMethod.invoke(cleaner);}} catch (Exception e) {// JDK版本兼容性处理}}
}

7. 最佳实践总结

7.1 开发阶段

  1. 对象生命周期管理

    • 及时释放不需要的引用
    • 使用局部变量替代成员变量
    • 避免在循环中创建大量临时对象
  2. 集合使用优化

    • 预估集合大小,避免频繁扩容
    • 选择合适的集合类型
    • 及时清理不使用的集合元素
  3. 字符串处理优化

    • 大量字符串拼接使用StringBuilder
    • 避免不必要的字符串创建
    • 合理使用字符串常量池

7.2 生产环境

  1. 监控体系建设

    • 配置GC日志
    • 设置GC监控告警
    • 定期分析GC报告
  2. 参数调优

    • 根据应用特点选择合适的收集器
    • 合理设置堆内存大小
    • 调整GC触发条件
  3. 性能测试

    • 压力测试验证GC性能
    • 长时间运行测试检查内存泄漏
    • 不同负载下的GC表现分析

结语

Java垃圾回收机制是一个复杂而精巧的系统,它在很大程度上简化了Java开发者的内存管理工作。但是,要想写出高性能的Java应用,深入理解GC原理和掌握调优技巧仍然是必不可少的。

随着Java版本的不断更新,垃圾回收技术也在持续进化。从传统的串行收集器到现代的ZGC和Shenandoah,Java的GC性能得到了显著提升。作为开发者,我们需要保持学习,跟上技术发展的步伐,在实际项目中灵活运用这些知识,为用户提供更好的体验。

记住,GC调优不是一蹴而就的过程,需要结合具体的应用场景、业务特点和性能要求进行综合考虑。只有在充分理解原理的基础上,才能做出正确的调优决策。


本文涵盖了Java垃圾回收的核心概念、算法原理、收集器对比和实践调优等内容。如果您在实际应用中遇到GC相关问题,建议结合具体场景进行分析,必要时可以寻求专业的性能调优服务支持。

相关文章:

Java垃圾回收机制详解:从原理到实践

Java垃圾回收机制详解&#xff1a;从原理到实践 前言 垃圾回收&#xff08;Garbage Collection&#xff0c;简称GC&#xff09;是Java虚拟机自动管理内存的核心机制之一。它负责自动识别和回收不再被程序使用的内存空间&#xff0c;从而避免内存泄漏和溢出问题。深入理解垃圾…...

thinkphp8.1 调用巨量广告API接口,刷新token

1、在mysql中建立表sys_token; CREATE TABLE sys_token (id int UNSIGNED NOT NULL,access_token varchar(50) COLLATE utf8mb4_general_ci NOT NULL,expires_in datetime NOT NULL,refresh_token varchar(50) COLLATE utf8mb4_general_ci NOT NULL,refresh_token_expires_in …...

物联网数据归档方案选择分析

最近在做数据统计分析。我在做数据分析时候,需要设计归档表。有两种方式, 方式1:年月日。 其中,日表是每小时数据,每台设备有24条数据 月表是每天数据,每台设备根据实际月天数插入 年表是每月数据,每台设备有12条数据。 方式2:年月日时。 其中,小时表,是每个设备每小…...

微服务架构下的服务注册与发现:Eureka 深度解析

&#x1f4e6; 一、引言 &#x1f310; 微服务架构中服务注册与发现的核心价值 在微服务架构中&#xff0c;服务注册与发现是支撑系统可扩展性、高可用性和动态管理的关键基础。 ✅ 核心价值解析 动态扩展与弹性伸缩 服务实例可随时上线/下线&#xff0c;无需手动更新配置&am…...

Qt/C++学习系列之QButtonGroup的简单使用

Qt/C学习系列之QButtonGroup的简单使用 前言QButtonGroup刨析源码 具体使用界面设计具体函数使用初始化信号与槽函数&#xff08;两种方式&#xff09; 总结 前言 在练手项目中&#xff0c;使用了QButtonGroup。项目需求有互斥的要求&#xff0c;在使用QRadioButton的基础上&a…...

CETOL 6σ v12.1 三维公差分析软件现已可供下载

一、新版本发布 德克萨斯州麦金尼 — 2025年6月5日 —Sigmetrix 宣布其最新版本的 CETOL 6σ 公差分析软件&#xff08;v12.1&#xff09;现已可供立即下载。公差分析在诸多方面为企业发展带来益处。它通过平衡质量与制造成本&#xff0c;助力企业提升盈利能力。企业还可借此缩…...

【JavaEE】Spring Boot项目创建

Spring Boot介绍 在学习Spring Boot之前&#xff0c;我们先来认识一下Spring Spring官方是这样介绍的&#xff1a; 可以看到&#xff0c;Spring让Java程序更加快速&#xff0c;简单和安全。Spring对于速度&#xff0c;简单性和生产力的关注使其成为世界上最流行的Java框架 Sp…...

KAG与RAG在医疗人工智能系统中的多维对比分析

1、引言 随着人工智能技术的迅猛发展,大型语言模型(LLM)凭借其卓越的生成能力在医疗健康领域展现出巨大潜力。然而,这些模型在面对专业性、时效性和准确性要求极高的医疗场景时,往往面临知识更新受限、事实准确性不足以及幻觉问题等挑战。为解决这些问题,检索增强生成(…...

车牌识别技术解决方案

在城市化进程不断加速的背景下&#xff0c;小区及商业区域的车辆管理问题日益凸显。为解决这一问题&#xff0c;车牌识别技术应运而生&#xff0c;成为提升车辆管理效率与安全性的关键手段。本方案旨在详细介绍车牌识别系统的基本原理、功能设计、实施流程以及预期效益&#xf…...

C/C++ 面试复习笔记(4)

1.在多线程的 Linux 程序中&#xff0c;调用系统函数&#xff08;如pthread_create 创建线程、pthread_mutex_lock 锁定互斥锁等&#xff09;可能会返回错误码。 与单线程环境相比&#xff0c;多线程环境下的错误处理有哪些需要特别注意的地方&#xff1f;请举例说明如何在多线…...

Unity 大型手游碰撞性能优化指南

Unity 大型手游碰撞性能优化指南 版本: 2.1 作者: Unity性能优化团队 语言: 中文 前言 在Unity大型手游的开发征途中,碰撞检测如同一位隐形的舞者,它在游戏的物理世界中赋予物体交互的灵魂。然而,当这位舞者的舞步变得繁复冗余时,便会悄然消耗宝贵的计算资源,导致帧率下…...

Git仓库的创建

Git服务器准备 假设Git所在服务器为Ubuntu系统&#xff0c;IP地址10.17.1.5。 一. 准备运行git服务的git用户&#xff0c;这里用户名就直接设定为git。 1. 创建一个git用户组&#xff0c;并创建git用户。 sudo groupadd git sudo useradd git -g git 2. 创建git用户目录&…...

从零到一:Maven 快速入门教程

目录 Maven 简介Maven 是什么为什么使用 Maven&#xff1f; 安装 Maven下载 Maven 配置 Maven解压文件配置本地仓库保存路径配置国内仓库地址 Maven 的核心概念了解 pom.xml 文件坐标依赖范围生命周期compileprovidedruntimetestsystemimport 依赖传递依赖排除依赖循环 继承1. …...

DDD架构实战 领域层 事件驱动

目录 核心实现&#xff1a; 这种实现方式的优势&#xff1a; 在实际项目中&#xff0c;你可能需要&#xff1a; 事件驱动往往是在一个微服务内部实现的 领域时间是DDD架构中比较常见的概念 在领域层内部的一个模型更改了状态或者发生了一些行为 向外发送一些通知 这些通…...

c# List<string>.Add(s) 报错:UnsupportedOperationException

在使用c#读取目录下指定格式文件目录后&#xff0c;使用List<string>.Add 来保存文件名时&#xff0c;出现UnsupportedOperationException错误&#xff0c;找了好久不知道问题出在哪里。 以下是错误代码&#xff1a; using (var fbd new FolderBrowserDialog{Descripti…...

postman基础

前言 本次 Chat 将结合业界广为推崇和使用的 RestAPI 设计典范 Github API&#xff0c;详细介绍 Postman 接口测试工具的使用方法和实战技巧。 在开始这个教程之前&#xff0c;先聊一下为什么接口测试在现软件行业如此重要&#xff1f; 为什么我们要学习 Postman&#xff1f;…...

python训练营day45

知识点回顾&#xff1a; tensorboard的发展历史和原理tensorboard的常见操作tensorboard在cifar上的实战&#xff1a;MLP和CNN模型 效果展示如下&#xff0c;很适合拿去组会汇报撑页数&#xff1a; 作业&#xff1a;对resnet18在cifar10上采用微调策略下&#xff0c;用tensorbo…...

B+树知识点总结

核心目标&#xff1a;减少磁盘 I/O 数据库系统&#xff08;如 MySQL&#xff09;的主要性能瓶颈通常在于磁盘 I/O&#xff08;读取和写入数据到物理硬盘的速度远慢于内存访问&#xff09;。B 树的设计核心就是最大限度地减少访问数据时所需的磁盘 I/O 次数。 一、B 树的基本结…...

Halcon透视矩阵

在 Halcon中&#xff0c;透视变换矩阵用于将图像从一个视角转换到另一个视角&#xff0c;常用于图像校正和几何变换。以下是计算透视变换矩阵的步骤及代码示例。 透视形变图像校正的步骤 对图像左简单的处理&#xff0c;分割要校正的区域&#xff1b;提取区域的顶点坐标信息&…...

SpringCloud——OpenFeign

概述&#xff1a; OpenFeign是基于Spring的声明式调用的HTTP客户端&#xff0c;大大简化了编写Web服务客户端的过程&#xff0c;用于快速构建http请求调用其他服务模块。同时也是spring cloud默认选择的服务通信工具。 使用方法&#xff1a; RestTemplate手动构建: // 带查询…...

007-nlohmann/json 项目应用-C++开源库108杰

本课为 fswatch&#xff08;第一“杰”&#xff09;的示例项目加上对配置文件读取的支持&#xff0c;同时借助 第三“杰” CLI11 的支持&#xff0c;完美实现命令行参数与配置文件的逻辑统一。 012-nlohmann/json-4-项目应用 项目基于原有的 CMake 项目 HelloFSWatch 修改。 C…...

移动端测试岗位高频面试题及解析

文章目录 一、基础概念二、自动化测试三、性能测试四、专项测试五、安全与稳定性六、高级场景七、实战难题八、其他面题 一、基础概念 移动端测试与Web测试的核心区别&#xff1f; 解析&#xff1a;网络波动&#xff08;弱网测试&#xff09;、设备碎片化&#xff08;机型适配&…...

gvim比较两个文件不同并合并差异

使用 gvim 比较两个文件的不同&#xff1a; 方式一&#xff0c;使用 gvim 同时打开两个待比较的文件。 比较通用方式是采用 gvim -d 选项&#xff0c;具体命令&#xff0c;如下&#xff1a; gvim -d <file1> <file2>方式二&#xff0c;先用 gvim 打开一个文件&am…...

App使用webview套壳引入h5(二)—— app内访问h5,顶部被手机顶部菜单遮挡问题,保留顶部安全距离

引入webview的页面添加safeAreaInsets&#xff0c;对weview的webviewStyles做处理 在myApp中改造 entry.vue代码如下 template><view class"entry-page" :style"{ paddingTop: safeAreaInsets.top px }"><web-view :webview-styles"we…...

Git GitHub Gitee

一、Git 是一个免费、开源的分布式版本控制系统。 版本控制&#xff1a;一种记录文件内容变化&#xff0c;以便将来查阅特定版本修订情况的系统。它最重要的就是可以记录文件修改历史记录&#xff0c;从而让用户可以看历史版本&#xff0c;方便版本切换。 1.和集中式版本控制…...

《深度体验 Egg.js:打造企业级 Node.js 应用的全景指南》

&#x1f680; 核心亮点&#xff1a;Koa 的二次觉醒 企业级基因&#xff1a;阿里多年双十一验证的框架稳定性插件化架构&#xff1a;config.plugins 实现功能模块即插即用渐进式演进&#xff1a;从 50 行代码到 5 万行代码的无缝扩容能力 &#x1f527; 实战配置解析&#xff…...

蓝桥杯2118 排列字母

问题描述 小蓝要把一个字符串中的字母按其在字母表中的顺序排列。 例如&#xff0c;LANQIAO 排列后为 AAILNOQ。 又如&#xff0c;GOODGOODSTUDYDAYDAYUP 排列后为 AADDDDDGGOOOOPSTUUYYY。 请问对于以下字符串&#xff0c;排列之后字符串是什么&#xff1f; WHERETHEREIS…...

Python应用break初解

大家好!作为 Python 初学者&#xff0c;控制循环的执行是编程中的基础技能之一。在本文中&#xff0c;我们将深入探讨break语句的用途和用法&#xff0c;帮助您更好地理解和掌握这一强大的工具。 定义: break是 Python 中的一个保留关键字&#xff0c;用于在循环中提前终止循环…...

PLSQLDeveloper配置OracleInstantClient连接Oracle数据库

PL/SQLDeveloper配置Oracle Instant Client连接Oracle数据库 文章目录 PL/SQLDeveloper配置Oracle Instant Client连接Oracle数据库 1. Oracle Instant Client下载与配置1. Oracle Instant Client下载2. Oracle Instant Client解压配置1. 解压2. 配置 2. PL/SQL Developer下载、…...

高股息打底+政策催化增强+永续经营兜底

通过分析农业银行&#xff08;政策红利高股息&#xff09;与长江电力&#xff08;垄断资源现金流堡垒&#xff09;的共性&#xff0c;提炼出以下策略框架&#xff1a; ​​1. 核心筛选标准​​ • 高股息防御性&#xff1a;股息率>3%&#xff0c;分红率稳定&#xff08;40%…...