JVM逃逸分析实战:优化内存分配与GC性能的核心技术

Isograph Reliability Workbench 14实战:从零构建故障树分析模型 本文以“马达不转”为例,手把手教你使用Isograph Reliability Workbench 14从零构建故障树分析模型。内容涵盖创建工程、定义顶事件、添加逻辑门基础事件等核心步骤,并介绍了模型检查定性定量分析,帮助你系统掌握这一强大的可靠性工程工具。 阅读详情

这次我们来看一个 JVM 逃逸分析的技术点。对于 Java 开发者来说,JVM 调优和性能优化是绕不开的话题,而逃逸分析(Escape Analysis)作为 JVM 即时编译器(JIT)的一项关键技术,直接关系到代码在运行时的内存分配和性能表现。它决定了对象是在堆上分配,还是在栈上分配,甚至是否可以被完全优化掉。理解它,对于写出高性能、低延迟的 Java 代码至关重要。

这篇文章不讲复杂的理论推导,重点放在“能不能用”、“怎么用”和“实际效果”上。我们会拆解逃逸分析的核心概念,通过代码示例直观展示其作用,并探讨如何利用 JVM 参数来观察和影响其行为。无论你是正在准备 JVM 面试,还是遇到了实际的内存飙升、GC 频繁问题,这篇文章都能提供直接的排查思路和优化方向。

1. 核心能力速览

在深入细节之前,我们先通过一个表格快速了解逃逸分析的核心要点,这能帮你快速判断它是否与你当前遇到的问题相关。

能力项 说明
技术本质 JVM 即时编译器(JIT)在编译时进行的一项分析,用于判断一个 新创建的对象 作用域 是否可能被方法外部或其它线程所引用。
核心优化 基于分析结果,JVM 可以实施三项关键优化: 栈上分配(Stack Allocation) 标量替换(Scalar Replacement) 同步消除(Lock Elision)
触发条件 由 JIT 编译器在 热点代码 (被频繁执行的代码段)编译为本地机器码时自动触发,无需开发者手动干预。
观察方式 主要通过 JVM 启动参数输出 JIT 编译日志来观察,例如 -XX:+PrintCompilation -XX:+PrintEscapeAnalysis (需调试版JVM)。
影响性能 成功应用优化可以 显著减少堆内存分配压力 降低垃圾回收(GC)频率 ,并 消除不必要的同步开销 ,从而提升程序吞吐量和降低延迟。
局限性 分析本身有开销;不是所有“未逃逸”的对象都能被优化(受JVM实现复杂度限制);在高复杂度的循环或调用链中可能失效。
适用场景 大量创建短生命周期临时对象的场景,如方法内的局部对象、循环体内创建的对象、作为中间计算结果的对象。
相关热词 JVM内存模型、JVM调优、GC垃圾回收、JVM面试题、内存飙升排查。

2. 适用场景与使用边界

逃逸分析是一项编译器优化技术,理解它适合谁、能解决什么问题,以及它的边界在哪里,比死记概念更重要。

适合谁?

  • 追求极致性能的开发者 :如果你的应用对延迟和吞吐量要求极高,理解逃逸分析可以帮助你从编译器层面优化代码风格。
  • 面临GC压力的系统维护者 :当监控发现 Young GC 频繁,或者堆内存中充斥着大量短命对象时,逃逸分析可能指出代码层面的优化方向。
  • JVM 学习与面试准备者 :这是深入理解 JVM 自动内存管理和 JIT 优化的关键一环,是区分普通开发和资深开发的知识点。

能解决什么问题?

  1. 减少无效对象分配 :将原本需要在堆上分配并最终由GC回收的短生命周期对象,改为在栈上分配或直接拆解为基本类型,对象随栈帧出栈而销毁,GC压力骤减。
  2. 消除无效同步 :如果分析发现某个锁对象(如 synchronized(obj) 中的 obj )不会逃逸出当前线程,那么相关的加锁/解锁操作会被完全移除,这在竞争不激烈的场景下能带来性能提升。
  3. 提升内存局部性 :栈上分配或标量替换后的数据,访问速度远高于堆内存,有利于CPU缓存命中,提升计算效率。

不适合什么场景?

  • 对象明确会逃逸 :如果对象作为方法返回值、赋值给类静态字段或实例字段、传递给其他线程等,它必然逃逸,分析不会带来优化。
  • 分析成本过高 :对于极其复杂、调用层级深、分支多的方法,JVM 可能为了编译速度而放弃深度逃逸分析。
  • 期望手动控制 :开发者无法通过代码直接命令 JVM “对这个对象做栈上分配”。优化由 JVM 全权决策,开发者能做的是写出利于分析的代码。

安全与合规边界 : 逃逸分析是 JVM 内部的纯技术优化,不涉及数据安全、隐私或版权问题。但需要注意,基于其优化(如锁消除)编写的代码,不应依赖锁的副作用(如内存可见性)来保证正确性,正确的并发应依赖于 volatile final java.util.concurrent 包下的工具。

3. 环境准备与前置条件

要观察和验证逃逸分析的效果,你需要一个可以运行 Java 程序并能够输出 JVM 诊断信息的环境。

  1. 操作系统 :主流的 Windows、Linux 或 macOS 均可。
  2. Java 开发工具包 (JDK) 必须使用 HotSpot JVM (Oracle JDK 或 OpenJDK)。建议使用 JDK 8 或更高版本。可以通过 java -version 命令确认。
    java -version
    # 输出应包含 “HotSpot” 字样,例如:
    # java version “1.8.0_381” Java(TM) SE Runtime Environment (build 1.8.0_381-b09) Java HotSpot(TM) 64-Bit Server VM (build 25.381-b09, mixed mode)
    
  3. IDE 或文本编辑器 :用于编写测试代码,如 IntelliJ IDEA、Eclipse 或 VS Code。
  4. JVM 诊断参数 :我们需要在启动 Java 程序时添加特定的 JVM 参数来开启编译日志和 GC 日志,以便观察。
    • -XX:+PrintCompilation :打印 JIT 编译事件。
    • -XX:+PrintGC -XX:+PrintGCDetails :打印垃圾回收详情,用于观察GC频率和内存分配变化。
    • -XX:+DoEscapeAnalysis :默认开启,无需显式指定。但有些文章提到用 -XX:+PrintEscapeAnalysis ,请注意, 在标准的 Oracle/OpenJDK 发布版中,此参数通常不可用 ,它是用于 JVM 调试版本的内部参数。我们的验证将主要通过 GC 日志和性能对比来间接观察。
  5. 性能观测工具(可选但推荐)
    • JConsole / VisualVM :图形化监控堆内存使用、GC 活动和线程状态。
    • Java Mission Control (JMC) :更强大的性能监控和诊断工具。
    • 命令行工具 jstat -gc <pid> 可以动态查看 GC 统计信息。

4. 逃逸分析原理与代码示例

理解了“是什么”和“为什么”之后,我们通过具体的代码来看“怎么做”。逃逸分析主要带来三种优化,我们逐一用代码说明。

4.1 栈上分配 (Stack Allocation)

概念 :如果一个对象被确定不会逃逸出当前方法(即方法外部无法引用到它),JVM 就有可能将这个对象分配在 栈帧 中,而不是堆里。栈帧随着方法调用结束而弹出,内存自动释放,无需垃圾回收器介入。

示例:未逃逸的对象

public class EscapeAnalysisDemo1 {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100_000_000; i++) {
            // 每次循环都创建一个新的`User`对象,但它只在`createUser`方法内部使用
            createUser(“User” + i, i);
        }
        long end = System.currentTimeMillis();
        System.out.println(“耗时:” + (end - start) + “ ms”);
    }

    private static void createUser(String name, int age) {
        // user 对象的作用域仅限于此方法,没有返回,没有赋值给外部变量。
        // 这是一个典型的“未逃逸”对象。
        User user = new User(name, age);
        // 可能对user做一些操作,但不会将其暴露出去
        // user.doSomething();
    }

    static class User {
        String name;
        int age;
        User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
}

分析 :在 createUser 方法中创建的 User 对象 user ,其引用没有逃逸出方法(没有被返回,也没有赋值给任何外部可见的变量)。对于这样的对象,JIT 编译器通过逃逸分析后,可能会尝试进行栈上分配。这意味着,在运行这段热点代码时,可能不会在堆中产生1亿个 User 对象,从而极大减轻了 GC 的压力。你可以通过对比开启和关闭逃逸分析时的 GC 日志和耗时来验证。

如何验证 :运行上述程序,并添加 -XX:+PrintGC 参数。理论上,如果栈上分配生效,你将看到极少的 Minor GC 事件。同时,可以尝试使用 -XX:-DoEscapeAnalysis 关闭逃逸分析,对比运行时间和GC次数。

4.2 标量替换 (Scalar Replacement)

概念 :这是栈上分配的一种“激进”形式。如果对象不仅没有逃逸,而且其内部结构可以被拆解(即“标量化”),那么 JVM 可能根本不为这个对象分配连续内存,而是将其成员变量(原始类型)直接存储在栈帧的局部变量表中,或者甚至直接存储在CPU寄存器中。

示例:可被标量替换的对象

public class EscapeAnalysisDemo2 {
    public static void main(String[] args) {
        Point p = allocatePoint(10, 20);
        System.out.println(“计算结果是:” + (p.x + p.y));
    }

    private static Point allocatePoint(int x, int y) {
        // point 对象没有逃逸,且其成员x, y是基本类型int。
        // JIT编译器可能将其优化为:直接在栈上使用两个int变量_x, _y。
        Point point = new Point(x, y);
        return point; // 注意:这里返回的是一个新的Point对象,但传入的point并未逃逸。
        // 更典型的例子是方法内计算后直接使用,不返回对象本身。
    }

    static class Point {
        int x;
        int y;
        Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
}

分析 :在 allocatePoint 方法中, point 对象没有逃逸(虽然返回了一个新的Point,但返回的不是 point 本身)。 Point 类只有两个 int 字段。经过逃逸分析和标量替换优化后,这段代码在机器码层面可能等价于:

private static int[] allocatePointOptimized(int x, int y) { // 概念上等价
    int _x = x;
    int _y = y;
    // 直接使用 _x 和 _y 进行计算
    return new int[]{_x, _y}; // 这里返回新对象,但原来的point对象已被“分解”
}

对象消失了,只剩下它的“标量”成分。这进一步减少了内存占用和访问开销。

4.3 同步消除 (Lock Elision)

概念 :如果逃逸分析能够证明,一个锁对象(例如,用在 synchronized 块中的对象)不会逃逸出当前线程,即其他线程永远不可能访问到这个锁对象,那么针对这个锁的同步操作就是多余的,JIT 编译器会将这些同步指令完全移除。

示例:可消除的同步锁

public class EscapeAnalysisDemo3 {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 1000; i++) {
            // append 方法是 synchronized 的
            sb.append(“a”);
        }
        System.out.println(sb.length());
    }
}

分析 StringBuffer append 方法是同步的。但在上面的代码中, sb 这个 StringBuffer 对象是在 main 方法的局部变量中创建和使用的,并且没有发布到其他线程(在这个简单示例中, main 是单线程)。因此,逃逸分析可以判定 sb 对象是“线程本地”的,不会发生线程间的竞争。那么,JIT 编译器在编译热点代码(循环体)时,就可能会将 append 方法内部的锁操作消除掉,从而提升性能。

对比 :你可以将 StringBuffer 替换为非同步的 StringBuilder 作为性能基准,然后对比使用 StringBuffer 在开启和关闭逃逸分析( -XX:+/-DoEscapeAnalysis )下的性能差异。在单线程场景下,经过锁消除优化后,两者性能可能非常接近。

5. 功能测试与效果验证

理论需要实践验证。我们将设计一个简单的测试,通过观察 GC 行为和运行时间来间接验证逃逸分析优化的效果。

5.1 测试目的

验证在大量创建短生命周期临时对象时,开启逃逸分析是否能有效减少 GC 活动,并提升程序性能。

5.2 测试代码

我们编写一个更易于观察的测试类:

public class EscapeAnalysisTest {
    private static final int ITERATIONS = 50_000_000; // 循环5000万次

    public static void main(String[] args) {
        // 预热,让JIT编译发生
        for (int i = 0; i < 10_000; i++) {
            createTempObject(i);
        }

        System.gc(); // 建议GC,清理预热阶段的对象
        try { Thread.sleep(1000); } catch (InterruptedException e) {}

        long startTime = System.nanoTime();
        long startFreeMem = Runtime.getRuntime().freeMemory();

        // 测试核心:循环创建大量临时对象
        for (int i = 0; i < ITERATIONS; i++) {
            createTempObject(i);
        }

        long endTime = System.nanoTime();
        long endFreeMem = Runtime.getRuntime().freeMemory();

        long durationMs = (endTime - startTime) / 1_000_000;
        long memoryUsed = startFreeMem - endFreeMem;

        System.out.println(“循环次数:” + ITERATIONS);
        System.out.println(“执行耗时:” + durationMs + “ ms”);
        System.out.println(“估算内存消耗(近似):” + memoryUsed / 1024 / 1024 + “ MB”);
    }

    /**
     * 创建一个临时对象,该对象没有逃逸出此方法。
     * 如果逃逸分析生效,此对象可能被栈上分配或标量替换。
     */
    private static void createTempObject(int id) {
        // TempObject 是一个简单的数据载体
        TempObject obj = new TempObject(id, “Temp-” + id);
        // 模拟一些使用,但绝不将obj暴露出去
        int hash = obj.hashCode(); // 调用方法不会导致逃逸
        // obj = null; // 显式置空在某些旧版本JVM中可能有提示作用,现代JVM中通常不需要
    }

    static class TempObject {
        int id;
        String name;
        TempObject(int id, String name) {
            this.id = id;
            this.name = name;
        }
    }
}

5.3 操作步骤与预期结果

  1. 编译运行 :将上述代码保存为 EscapeAnalysisTest.java 并编译。

    javac EscapeAnalysisTest.java
    
  2. 开启逃逸分析测试 (默认开启):

    java -XX:+PrintGC -Xms256m -Xmx256m EscapeAnalysisTest
    
    • -XX:+PrintGC :打印每次GC事件。
    • -Xms256m -Xmx256m :将堆内存限制在256MB,更容易观察到GC行为。 预期结果 :由于逃逸分析生效, TempObject 对象可能被优化,堆内存分配压力小。因此,控制台输出的 GC 日志行数应该非常少(甚至没有) ,同时“估算内存消耗”会远小于理论值(5000万个对象 * 每个对象开销)。执行耗时也相对较短。
  3. 关闭逃逸分析测试

    java -XX:+PrintGC -Xms256m -Xmx256m -XX:-DoEscapeAnalysis EscapeAnalysisTest
    
    • -XX:-DoEscapeAnalysis :显式关闭逃逸分析。 预期结果 :每次循环都会在堆上创建一个真实的 TempObject 对象。很快堆内存就会被填满,触发频繁的 Minor GC 。控制台会刷出大量的 [GC (Allocation Failure) ...] 日志。程序执行耗时 会显著长于 开启逃逸分析的情况,因为大量时间花在了内存分配和垃圾回收上。“估算内存消耗”的数值也会更大。

判断成功的标准 :对比两次运行的输出。如果关闭逃逸分析后,GC 日志明显增多、运行时间显著增加,则从侧面证明了逃逸分析在优化内存分配、减少 GC 方面起到了关键作用。

6. 接口 API 与批量任务(概念延伸)

逃逸分析是 JVM 内部的、自动的优化机制,它本身没有对外的 API。但是,理解它对我们设计高性能的“接口”和“批量任务”有重要指导意义。

对微服务/RPC接口的启示 : 在实现一个高并发的 API 接口时,接口方法内部可能会创建大量临时对象(如 DTO 转换、字符串拼接、集合操作等)。如果这些对象被设计成不会逃逸(例如,作为局部变量在方法内使用并销毁),那么 JVM 的逃逸分析就有机会优化它们。这要求我们在编码时:

  • 尽量避免在热点方法中返回或修改外部传入的可变对象(可能导致逃逸)。
  • 对于只读的、方法内使用的数据,优先使用局部变量和基本类型。
  • 谨慎使用同步块,如果锁对象是局部创建的且不逃逸,则可能被消除。

对批量任务处理的启示 : 在批处理任务(如处理一个文件中的每一行、计算大量数据条目)中,循环体内创建对象是常态。这正是逃逸分析大显身手的地方。

// 好的模式:对象在循环体内创建和使用,未逃逸
public void processBatch(List<Data> batch) {
    for (Data data : batch) {
        // Processor 对象在每次迭代中创建,只在本轮循环使用
        Processor processor = new Processor(data);
        Result result = processor.calculate(); // calculate 方法不使processor逃逸
        storeResult(result);
    }
}

// 可能不利于优化的模式:对象逃逸出了循环作用域
public void processBatchPoor(List<Data> batch) {
    Processor globalProcessor = null; // 对象引用逃逸到循环外
    for (Data data : batch) {
        globalProcessor = new Processor(data); // 每次赋值,上一个对象可能还未“死”,影响分析
        Result result = globalProcessor.calculate();
        storeResult(result);
    }
}

最佳实践 :在批量任务的循环体内,尽量让临时对象的生命周期局限于单次迭代。避免将循环内创建的对象赋值给循环外部的引用。

7. 资源占用与性能观察

逃逸分析优化的最终目的是降低资源占用和提升性能。我们可以从以下几个维度观察:

  1. GC 频率与暂停时间 :这是最直接的指标。使用 -XX:+PrintGCDetails -XX:+PrintGCDateStamps 参数运行你的程序,观察 Full GC Young GC 的次数和耗时。成功优化后,GC 次数应大幅减少。
  2. 堆内存使用模式 :使用 jstat -gc <pid> 1000 (每秒采样一次)观察堆内存各区域(Eden, Survivor, Old Gen)的容量和使用量变化。优化后,Eden 区的增长和清理频率会变慢。
  3. CPU 利用率与吞吐量 :减少 GC 意味着更多的 CPU 时间用于执行业务逻辑。可以使用操作系统工具(如 top , htop )或 APM 工具观察应用的整体 CPU 利用率和吞吐量(如 QPS)。
  4. JIT 编译日志 :虽然 -XX:+PrintEscapeAnalysis 在标准版中不可用,但 -XX:+PrintCompilation 可以让你看到哪些方法被编译成了本地代码。热点方法被编译,是逃逸分析发生的前提。

如何降低“逃逸”可能性以助力优化?

  • 方法局部化 :尽可能在方法内部创建和使用对象。
  • 避免外部暴露 :不要将内部创建的临时对象赋值给类字段、静态变量或作为返回值(除非必要)。
  • 使用不可变对象 :不可变对象(如 String )的语义更清晰,更容易被分析。
  • 简化方法体 :过于复杂的方法(深度递归、大量分支)会增加分析难度,可能使 JVM 放弃优化。

8. 常见问题与排查方法

在实践中,你可能会遇到一些与内存和性能相关的问题,逃逸分析的知识可以帮助你排查。

问题现象 可能原因 排查方式 解决方案与思考
Young GC 异常频繁 系统产生了大量短生命周期对象,且逃逸分析未能优化(对象确实逃逸或分析失败)。 1. 使用 -XX:+PrintGCDetails 观察 GC 日志。
2. 使用内存分析工具(如 Eclipse MAT, JProfiler)抓取堆转储,分析数量最多的对象类型及其引用链。
1. 检查热点代码,确认创建的临时对象是否真的必要。
2. 重构代码,减少对象逃逸(见第7节建议)。
3. 考虑使用对象池(如 Apache Commons Pool)复用重量级对象,但需权衡复杂度。
关闭逃逸分析后性能下降不明显 1. 测试用例不当,对象本身已逃逸,优化本就未发生。
2. 测试的循环次数不够,未触发JIT编译。
3. GC 压力本身不是瓶颈。
1. 检查测试代码,确保对象是真正的“未逃逸”。
2. 增加循环次数或进行充分的JVM预热。
3. 使用 -XX:+PrintCompilation 确认热点方法已被编译。
设计更精准的微基准测试(可使用 JMH 框架)。确保测试聚焦于对象分配本身,避免其他开销干扰。
同步代码块在单线程下依然很慢 锁对象可能逃逸了(例如是类字段),导致锁消除优化未能生效。 检查 synchronized 块中使用的锁对象的作用域。是否被多个方法共享?是否可能被其他线程访问? 如果确认该锁在特定场景下是线程局部的,可以尝试将锁对象范围缩小到方法内部(如 Object lock = new Object(); ),但需仔细评估线程安全性。
不确定某段代码是否被优化 缺少直接的观察手段。 1. 使用 -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly (需要HSDIS库)查看汇编代码,但对大多数开发者门槛过高。
2. 最实际的方法 :通过对比性能数据和GC日志进行间接验证。
关注宏观效果。如果通过代码重构(使对象不逃逸)后,GC频率下降、吞吐量上升,那就说明优化方向是正确的。

9. 最佳实践与使用建议

将逃逸分析的知识转化为日常开发中的好习惯:

  1. 优先使用局部变量 :在方法内部完成计算和操作,避免不必要的字段赋值和对象传递。
  2. 警惕“无意逃逸” :最常见的无意逃逸是将方法内创建的对象添加到方法外传入的集合(如 list.add(new Item()) )。这会导致对象逃逸。如果集合是局部创建的,并在方法内使用后废弃,则不会逃逸。
  3. 区分“小对象”与“大对象” :逃逸分析主要针对大量创建的小对象(如 Point , OrderItem )。对于大对象(如大数组、缓存对象),优化收益有限,应关注其他内存管理策略。
  4. 不要为了优化而过度设计 :逃逸分析是 JVM 的“锦上添花”。首先保证代码的正确性、清晰性和可维护性。在性能成为明确瓶颈后,再以此为指导进行有针对性的优化。
  5. 结合其他 JVM 优化 :逃逸分析与 方法内联(Method Inlining) 循环展开(Loop Unrolling) 等 JIT 优化协同工作。保持方法粒度适中、循环清晰,有利于这些优化的进行。
  6. 升级 JDK 版本 :新的 JDK 版本中,JIT 编译器(C1, C2)的优化能力在持续增强,包括逃逸分析算法。使用较新的 LTS 版本(如 JDK 11, 17, 21)通常能获得更好的运行时性能。

10. 总结与下一步

逃逸分析是 JVM 为开发者默默提供的“性能加速包”。它通过静态分析,在运行时智能地决定对象的分配位置和同步操作的必要性,从而减少内存分配开销和消除不必要的锁竞争。

对于开发者而言,最重要的不是去操控它,而是理解其原理,并以此指导我们编写出对编译器更“友好”的代码——即 尽可能减少不必要的对象逃逸 。当你发现应用存在 GC 频繁、内存分配速率高的问题时,从逃逸分析的角度审视热点代码,往往能找到优化的突破口。

下一步可以做什么?

  1. 使用 JMH 进行基准测试 :Java Microbenchmark Harness (JMH) 是 Oracle 推荐的进行 Java 微基准测试的工具。用它来精确测量代码片段在开启/关闭逃逸分析下的性能差异,结果更可靠。
  2. 深入学习 JIT 编译日志 :探索更多 JVM 参数,如 -XX:+PrintInlining (查看方法内联)、 -XX:+LogCompilation (输出更详细的编译日志到文件),结合工具(如 JITWatch)进行可视化分析。
  3. 研究 GraalVM :GraalVM 提供了一个用 Java 编写的高性能 JIT 编译器,它对逃逸分析等优化有新的实现,并且有时可以作为 HotSpot 的替代品,可能带来不同的性能特性。
  4. 关联其他 JVM 知识点 :将逃逸分析与 JVM 内存模型(JMM) 垃圾回收算法(如 G1, ZGC) JVM 调优参数 结合起来,形成完整的性能优化知识体系。

理解逃逸分析,是你从“会写Java代码”迈向“了解Java程序如何运行”的重要一步。建议将文中的示例代码实际运行一遍,观察GC日志的变化,这种直观的感受比阅读十篇文章更有价值。

手把手教你学Simulink——基于Simulink的蚁群算法路径规划仿真建模示例 路径规划是机器人导航系统中的核心任务之一,旨在为机器人找到一条安全且高效的路径,使其能够从起点到达目标点,同时避免障碍物发生碰撞。在开始之前,需要定义工作环境(例如地图),包括起点、终点以及可能存在的障碍物位置。通过本指南,我们介绍了如何基于Simulink结合MATLAB实现蚁群算法的路径规划仿真建模。 阅读详情

相关推荐

Yolo8行人车辆目标检测实战分享 完整项目代码可直接运行

Yolo8行人车辆目标检测实战分享 完整项目代码可直接运行

1265

重提URL Rewrite(1):IISASP.NET

之前觉得这个话题已经被谈滥了。URL Rewrite早已经被广大开发人员所接受,网上关于URL Rewrite的组件和文章也层出不穷,但是总是让我感觉意犹未尽,于是最终还是忍不住提笔写了这系列文章。这些文章不会谈论URL Rewrite的价值意义,而只会谈论纯技术的内容。文章中也不会有详尽地实现分析,而是结合了我的经验,从应用角度来讲解这个话题。您已经知道的,您还不知道的,...

weixin_34306446的博客 216

少儿学编程系列--使用递归画出龙形曲线DragonCurve

龙形曲线(Dragon Curve) 龙形曲线(Dragon Curve)又叫分形龙,是一种自相似碎形曲线的统称,因形似龙的蜿蜒盘曲而得名 使用黄金比例画龙形曲线 根据黄金比例画一条龙曲线。每次递归,第一步向左转32.89度,移动0.74倍原距离;第二个递归步骤向右转46.99度,移动原来距离的0.55倍。请查看这个网页(https://larryriddle.agnesscott.org/ifs/heighway/goldenDragon.htm)了解关于龙形黄线数学的细节 龙形曲线效果 .

penriver的博客 2269

在ASP.NET MVC中使用IIS级别的URL Rewrite

大约一年半前,我在博客上写过一系列关于URL Rewrite的文章(2、3、4),把ASP.NET平台上进行URL Rewrite的方式和各自地特点进行了较为详细的描述。应该来说,已经讲的非常具体,可以应对90%的情况。其实IIS Rewrite的原理非常容易理解,进行一些简单的变化和推断之后,便可以得出一些问题的原因和解决方案。现在我们就来看一个真实案例:在ASP.NET MVC中使用IIS级别的URL Rewrite。 在当时的文章中我谈到,URL Rewrite分有IIS级别和ASP.NET两种级别,

极客神殿 636

虚拟主机ASP.NET实现Rewrite功能的方法

seo优化的时候,有的网站不可以生成静态的页面 所有就要用到伪静态啦! 今天来看下 asp.net伪静态的写法 在.net程序中实现Rewrite功能可以在webconfig中的system.webServer节点中加入以上内容就可以实现转换。 <rewrite> <rules> <rule n...

weixin_34138377的博客 267

在ASP.NET中实现Url Rewriting

1.1. 概要分析如何使用微软提供的ASP.NET来对动态产生的URL地址进行网址重写。网址重写是实现一种截取网址请求并将其进行处理后重新指向到一个指定的网址的过程。作者本人在对各种实现网址重写的技术进行研究和探讨后得出的经验和方法,希望能对您有所帮助。1.2. 内容简介稍微花点时间看一看你做的网站里头的URL地址,你看到类似这样的地址吗http://yoursite.com/info

Mark的专栏 784

asp伪静态化/asp静态化/ASPURL重写/IIS Rewrite/RewriteRule/

asp伪静态化/asp静态化/ASPURL重写/IIS Rewrite/RewriteRule/asp伪静态化/asp静态化/ASPURL重写/IIS Rewrite/RewriteRule/rewrite规则/ISAPI_Rewrite.ISAPI_Rewriteasp动态页面静态化ISAPI_Rewrite是一个强大的基于正则表达式的URL处理引擎。它非常类似于Apaches mod_R

ferq1的专栏 1692

艾伟:重提URL Rewrite(1):IISASP.NET

  之前觉得这个话题已经被谈滥了。URL Rewrite早已经被广大开发人员所接受,网上关于URL Rewrite的组件和文章也层出不穷,但是总是让我感觉意犹未尽,于是最终还是忍不住提笔写了这系列文章。这些文章不会谈论URL Rewrite的价值意义,而只会谈论纯技术的内容。文章中也不会有详尽地实现分析,而是结合了我的经验,从应用角度来讲解这个话题。您已经知道的,您还不知道的,别处已经讲过的,或...

weixin_34301307的博客 98

艾伟:重提URL Rewrite(3):在URL Rewrite后保持PostBack地址

  在进行了URL Rewrite之后,经常会遇到的问题就是页面中PostBack的目标地址并非客户端请求的地址,而是URL Rewrite之后的地址。以上一篇文章中的重写为例: &lt;rewriter&gt;  &lt;rewrite url="^/User/(\d+)$" to="~/User.aspx?id=$1" processing="stop" /&gt;  &lt;rewrite ...

weixin_33847182的博客 104

艾伟:重提URL Rewrite(2):使用已有组件进行URL Rewrite

  可能已经没有人会使用上一篇文章中的方法进行URL Rewrite了,因为提供URL Rewrite的组件早已铺天盖地了。   ASP.NET级别的URL Rewrite组件的原理很简单,其实只是监听BeginRequest事件,并且根据配置来决定目标URL。在我之前接触过的项目中,发现使用URLRewriter作为URL Rewrite组件的频率非常高,我想可能是因为那是微软提供的东西吧。  ...

weixin_34404393的博客 88

ASP之URL重写

ASP之URL重写下载ISAPI_Rewrite解压到任意文件夹安装,给IIS用户读取权限,在IIS右键点击一个站点,属性,ISAPI筛选器,添加,筛选器名称一定得叫 re,可执行文件就点浏览找到你解压的目录Rewrite.dll加上即可。  重启IIS后应该就能支持URL重写规则了。  需要修改解压目录中的httpd.ini这个文件RewriteRule /info_(/d+)/

huangxiaohu888的专栏 709

Asp URL Rewrite

IIS下安装配置ISAPI Rewrite,实现asp的静态化的方法! 最近一直在搞俺小站的搜索引擎优化,效果还可以,从10月底的每天40IP上升到现在每天200多个。不过流量还是太少了。据说把动态的页生成静态对排名更有利。俺就来试试看。还好服务器是自己的,可以随便弄。 1、下载安装IIS下的ISAPI Rewrite。 下载:http://www.isapirewrite.com/down...

抽刀断水 198

【微软官方】在 ASP.NET 中执行 URL 重写rewriter

在 ASP.NET 中执行 URL 重写 发布日期 : 8/23/2004 | 更新日期 : 8/23/2004 Scott Mitchell 4GuysFromRolla.com 适用范围: Microsoft® ASP.NET 摘要:介绍如何使用 Microsoft ASP.NET 执行动态 URL 重写。URL 重写是截取传入 Web 请求并自动将请求重定向到其他 UR

Zfrong繁荣的IT技术、IT项目、解决方案专栏Blog-上海 994

asp.net实现URL重写

简单介绍了在asp.net中实现url的重写。

2301_76661653的博客 623

ASP.NET Core 实战:深入理解URL重定向重写机制

ASP.NET Core 实战:深入理解URL重定向重写机制 URL重定向(Redirect)和重写(Rewrite)是Web开发中常用的技术手段,它们可以帮助我们优化网站结构、改善用户体验并提升SEO效果。本文将基于一个实用的ASP.NET Core项目,详细解析URL重定向重写的实现方式。 URL重定向重写的基本概念 在Web开发中,URL重定向和重写是两种不同的技术: 重定向(Re...

gitblog_00611的博客 457

ASP.NET 使用 IIS URL Rewrite 模块实现伪静态

在做网站的时候为了让网页地址友好一点经常需要使用到 URL 重写,比如 https://www.baidu.com/a?b=1&c=2 这样的地址就不是很友好,也不方便记忆,如果是 https://www.baidu.com/a/1/2 这样的地址就比较好记了,那么在 IIS 下如何实现这种功能呢?IIS 默认是没有安装 URL 重写工具的,所以必须要自己下载安装。 URL R...

a495023351的博客 412

asp.net mvc、asp.net url重写 Url Rewrite

最近在做一个cms手机网站,由于内容不是很多,很快做完了。空闲的时间考虑seo,想把url全部写成静态的。在查资料的时候,发现 UrlRewrite.Net这个组件非常的不错,尝试了下结果达到了理想的效果。 1、通过nuget安装该类库; 2、注册为后缀html的路由; 3、web.config配置; <!--在 configuration 节点下配置--> <configSections> <section name="CustomConfiguration" typ

极客神殿 1175

rewrite规则用法和示例

rewrite规则用法和示例

qq_34953582的博客 922

ASP.NET中URL Rewrite的实现方法

http://developer.51cto.com/art/200901/104833.htm 在一个ASP.NET应用中如果要进行URL Rewrite,那么一般就是在BeginRequest事件中用HttpContext的RewritePath方法,将该请求重新“定位”至一个目标URL。例如我们就可以在Global.asax中重写Application_BeginRequ...

170

ASP.NET伪静态 UrlRewrite(Url重写) 实现和配置------转载

ASP.NET伪静态 UrlRewrite(Url重写) 实现和配置 核心提示:大家一定经常在网络上看到很多网站的地址后缀都是用XX.HTML或者XX.ASPX等类似静态文件的标示来操作的吧,那么大家有怀疑过他真的是一个一个的静态生成的文件么,静态文件的生成的缺有好有坏,对于访问量很大的网站,利用静态文件可提高服务器的运行稳定和速度... 大家一定经常在网络上看到很多网站的...

weixin_30902675的博客 238

(151页PPT)G86华为以客户为中心的销售组织能力建设.pptx

(151页PPT)G86华为以客户为中心的销售组织能力建设.pptx

精华隆安防报警软件.rar

精华隆安防报警软件.rar

上一篇: 字节跳动Java后端面试核心考点与避坑指南
下一篇: Deepfake音频检测:声纹比对与频谱分析技术详解
weixin_33827731
博客等级 码龄11年 5149粉丝 644原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值