JVM 堆

堆的核心概述

  • 一个 JVM 实例只存在一个堆内存,堆也是 Java 内存管理的核心区域
  • Java 堆区在 JVM 启动的时候即被创建,其空间大小也就确定了。是 JVM 管理的最大一块内存空间
  • 堆可以处于物理上不连续的内存空间中,但是在逻辑上它应该被视为连续
  • 所有的线程共享 Java 堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer, TLAB)
  • 所有的对象实例以及数组都应该在运行时分配在堆上
  • 数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置
  • 在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除
  • 堆,是 GC (Garbage Collection, 垃圾收集器) 执行垃圾回收的重点区域
/**
 * 设置堆大小 -Xms10m -Xmx10m
 */
public class HeapDemo {
    public static void main(String[] args) {
        System.out.println("start ...");

        try{
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end ...");
    }
}

/**
 * 设置堆大小 -Xms20m -Xmx20m
 */
public class HeapDemo1 {
    public static void main(String[] args) {
        System.out.println("start ...");

        try{
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end ...");
    }
}

HeapDemo 在 jvisualvm 查看:

HeapDemo1 在 jvisualvm 查看:

实例对象分配位置:

public class SimpleHeap {
    private int id;

    public SimpleHeap(int id) {
        this.id = id;
    }

    public void show(){
        System.out.println("My Id is " + id);
    }

    public static void main(String[] args) {
        SimpleHeap s1 = new SimpleHeap(1);
        SimpleHeap s2 = new SimpleHeap(2);

        int[] arr = new int[10];
        Object arr1 = new Object[10];
    }
}

内部结构细分:

Java 版本呢Code名称说明
Java 7 及之前Young Generationn Space新生区Young / New, 又被划分为 Eden区和 Survivor 区
Tenure generation space养老区Old / Tenure
Permanent space永久区Perm
Java 8 及之后Young Generationn Space新生区Young / New, 又被划分为 Eden区和 Survivor 区
Tenure generation space养老区Old / Tenure
Meta Space元空间Meta

设置堆内存大小与OOM

  • Java 堆区用于存储 Java 对象实例,那么堆的大小在 JVM 启动时就已经设定好了,可以通过选项 "-Xmx" 和 "-Xms" 来进行设置, "-Xms" 用于表示堆区的起始内存,等价于 -XX:InitialHeapSize、"-Xmx" 则用于表示堆区的最大内存,等价于 -XX:MaxHeapSize
  • 一旦堆区中的内存大小超过 "-Xmx" 所指定的最大内存时, 将会抛出 OutOfMemoryError 异常
  • 通常情况下会将 -Xms 和 -Xmx 两个参数配置相同的值, 其目的是为了能够在 java 垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能
  • 初始内存大小:物理内存大小 / 64
  • 初始最大内存大小: 物理电脑内存大小 /4 
public class HeapSpaceInitial {
    public static void main(String[] args) {
        // 返回 Java 虚拟机中的堆内存总量
        long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;

        // 返回 Java 虚拟机试图使用的最大堆内存量
        long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;

        System.out.println("-Xms:" + initialMemory + "M");
        System.out.println("-Xmx:" + maxMemory + "M");

        System.out.println("系统内存大小为:" + initialMemory * 64 * 1024 + "G");
        System.out.println("系统内存大小为:" + maxMemory * 4 * 1024 + "G");
// 执行 jstat -gc 20332 需要进行延迟, -XX:+PrintGCDetails 不需要
//        try{
//            Thread.sleep(1000000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
    }
}

由于 S0 和 S1 仅能一个存储,所以计算时会比设置的少一部分空间

 OutOfMemory 举例:

import java.util.ArrayList;
import java.util.Random;

public class OOMtest {
    public static void main(String[] args) {
        ArrayList<Picture> list = new ArrayList<>();
        while (true) {
            try{
                Thread.sleep(20);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }

            list.add(new Picture(new Random().nextInt(1024 * 1024)));
        }
    }
}

class Picture{
    private byte[] pixels;

    public Picture(int length) {
        this.pixels = new byte[length];
    }

}

年轻代与老年代

  • 存储在JVM 中的Java 对象可以划分为两类: 一类是生命周期较短的瞬时对象、这类对象的创建和消亡都非常迅速,另一类对象的生命周期却非常长,在某些极端的请开给你下还能够与 JVM 的生命周期保持一致
  • Java 堆区进一步细分的话,可以划分为年轻代(YoungGen) 和 年老代(OldGen)
  • 其中年轻代又可以划分为 Eden空间、Survivor0 空间和 Survivor1 空间(有时也叫做 from 区、to 区)

配置新生代和老年代在堆结构的占比:

  • 默认 -XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的 1/3
  • 可以修改 -XX:NewRatio=4,表示新生代占1,老年代占4,新生代占整个堆的 1/5
  •  在HotSpot 中,Eden 空间和另外两个 Servivor 空间缺省所占的比例是 8:1:1, 可以通过 -XX:SurvivorRatio=8 进行调整比例空间,默认值是8,实际值是6,需要显示进行配置
  • 可以使用选项 "-Xmn" 设置新生代最大内存大小,如果与设置 NewRatio 存在冲突,则以该配置为准 
/**
 * -Xms600m -Xmx600M
 */
public class EdenSurvivorTest {
    public static void main(String[] args) {
        System.out.println("我只是来打酱油~");
        try{
            Thread.sleep(1000000);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

图解对象分配过程

概述:

为新对象分配内存是一件非常严谨和复杂的任务, JVM 设计者不仅需要考虑内存如何分配、在哪里分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需要考虑 GC 执行完内存回收后是否在内存空间中产生内存碎片

  • new 的对象先放伊甸园区。此区有大小限制
  • 当伊甸园的空间填满时,程序又需要创建对象,JVM 的垃圾回收器堆伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊甸园区
  • 然后将伊甸园中的剩余对象移动到幸存者0区
  • 如果再次触发垃圾回收,此时上次幸存下来的放到幸存者0区的,如果没有回收,就会放到幸存者1区
  • 如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区
  • 默认 15次后,进入养老区,可以通过参数 -XX:MaxTenuringThreshold=<N> 进行设置

图解:

总结:

  • 针对幸存者 s0, s1区的总结: 复制之后有交换, 谁空谁是 to
  • 关于垃圾回收: 频繁在新生区收集,很少在养老区收集,几乎不再永久区 / 元空间收集

特殊情况:

常用调优工具:

  • JDK 命令行
  • Eclipse : Memory Analyzer Tool
  • Jconsole
  • VisualVM
  • Jprofiler
  • Java Flight Recorder
  • GCViewer
  • GC Easy

Minor GC、Major GC、Full GC

JVM 进行 GC时,并非每次都对上面三个内存(新生代、老年代; 方法区) 区域一起回收的,大部分时候回收的都是指新生代

针对 HotSpot VM 的实现,它里面的 GC 按照回收区域又分为两大种类型: 一种是部分收集 (Partial GC), 一种是整堆收集 (Full GC)

类型名称说明
部分收集Minor GC / Y只是新生代的(Eden \ s0, s1)垃圾收集
Major GC / Old GC只是老年代的垃圾收集, 只有CMS GC 会有单独收集老年代的行为。很多时候 Major GC 会和 Full GC 混肴使用,需要具体分辨是老年代回收还是整堆回收
Mixed GC收集整个新生代以及部分老年代的垃圾收集,目前只有 G1 GC 会有这种行为
整堆收集Full GC收集整个 Java 堆和方法区的垃圾收集

 年轻代 GC(Minor GC) 触发机制:

  • 当年轻代空间不足时,就会触发 Minor GC, 这里的年轻代满指的是 Eden 代满, Survivor 满不会引发 GC
  • 因为 Java 对象 大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度比较快
  • Minor GC 会引发 STW, 暂停其他用户的线程,等垃圾回收结束,用户线程才恢复运行

老年代 GC(Major GC / Full GC) 触发机制:

  • 指发生在老年代的 GC,对象从老年代消失时,我们说 "Major GC" 或 "Full GC" 发生了
  • 出现了 Major GC, 经常会伴随至少一次的 Minor GC(但非绝对的,在 Parallel Scavenge 收集器的收集策略里就有直接进行 Major GC 的策略选择过程),即在老年代空间不足时,会先尝试触发 Minor GC。如果之后空间还不足,则触发 Major GC
  • Major GC 的速度一般会比 Minor GC 慢10倍以上,STW 的时间更长
  • 如果 Major GC 后,内存还不足,就报 OOM了

Full GC 触发机制:

  • 调用 System.gc()时,系统建议执行 Full GC, 但是不必然执行
  • 老年代空间不足
  • 方法区空间不足
  • 通过 Minor GC后进入老年代的平均大小大于老年代的可用内存
  • 由Eden区、survivor space0(From Space)区向 survivor space1(To Space)区复制时,对象大于 To Space 可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

GC 日志查看:

 VM 配置 -XX:+PrintGCDetails :

import java.util.ArrayList;
import java.util.List;

public class GCTest {
    public static void main(String[] args) {
        int i = 0;
        try{
            List<String> list = new ArrayList<>();
            String a = "hello world";
            while(true) {
                list.add(a);
                a = a + a;
                i++;
            }
        }catch (Throwable t){
            t.printStackTrace();
            System.out.println("遍历次数为: " + i);
        }
    }
}

堆空间分代思想

分代是为了优化 GC 性能

内存分配策略

  • 如果对象在 Eden, 出生并经过第一次 MinorGC 后仍然存活,并且没有被 survivor 容纳的话,将被移动到 survivor 空间中,并且对象年龄设为1。对象在 survivor 区种每熬过一次 MinorGC, 年龄就增加 1 岁,当它的年龄增加到一定程度(默认为15岁,其实每个 JVM、每个 GC 都有所不同)时,就会被今生到老年代种
  • 对象今生老年代的年龄阈值,可以通过选项 -XX:MaxTenuringThreshold 来设置

针对不同年龄段的对象分配原则:

  • 优先分配到 Eden
  • 大对象直接分配到老年代,尽量避免程序中出现过多的大对象
  • 长期存活的对象分配到老年代
  • 动态对象年龄判断,如果Survivor 区中相同年龄的所有对象总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄
  • 空间分配担保: -XX:HandlePromotionFailure

为对象分配内存:TLAB(Thread Local Allocation Buffer)

  • 堆区的线程共享区域,任何线程都可以访问到堆区中的共享数据
  • 由于对象实例的创建在 JVM 中非常频繁,因此在并发环境下从堆中划分内存空间是线程不安全的
  • 为避免多个线程操作同一个地址,需要使用加锁等机制,进而影响分配速度

什么是 TLAB:

  • 从内存模型而不是垃圾收集的角度,对 Eden 区域继续进行划分, JVM 为每个线程分配了一个私有缓存区域,它包含在 Eden 空间内
  • 多线程同时分配内存时,使用 TLAB 可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称为快速分配策略
  • 据我所知所有 OpenJDK 衍生出来的 JVM 都提供了 TLAB 的设计
  • 尽管不是所有的对象实例都能够在TLAB 中成功分配内存,但 JVM 确实是将 TLAB 作为内存分配的首选
  • 在程序中,开发人员可以通过选项 "-XX:UseTLAB" 设置是否开启 TLAB 空间
  • 默认情况下, TLAB 空间的内存非常小,仅占有整个 Eden空间的 1%, 当然我们可以通过选项 "-XX:TLABWasteTargetPercent" 设置 TLAB 空间所占用 Eden 空间的百分比大小
  • 一旦对象在 TLAB 空间分配内存失败时, JVM 就会尝试着通过使用加锁机制确保数据操作的原子性,从而直接在 Eden 空间中分配内存

堆空间的参数设置

参数说明
-XX:+PrintFlagsInitial查看所有的参数的默认初始值
-XX:+PrintFlagsFinal查看所有的参数的最终值(可能会存在修改,不再是初始值)
jinfo -flag 参数 进程IDCMD, 查看当前运行进程某参数设置情况
-Xms初始堆空间内存(默认为物理内存的 1/64)
-Xmx最大堆空间内存(默认为物理内存的 1/4)
-Xmn设置新生代的大小(初始值及最大值)
-XX:NewRatio配置新生代和老年代在堆结构的占比
-XX:SurvivorRatio设置新生代中 Eden 和 s0 / s1 空间的比例
-XX:MaxTenuringThreshold设置新生代垃圾的最大年龄
-XX:+PrintGCDetails输出详细的 GC处理日志
-XX:+PrintGC打印 gc 简要信息
-XX:HandlePromotionFailure是否设置空间分配担保,jdk7后已失效 变为只要老年代的连续空间大于新生代总大小或者历次晋升的平均大小就会进行 Minor GC,否则将进行 Full GC

堆是分配对象的唯一选择吗?

如果经过逃逸分析(Escape Analysis) 后发现,一个对象并没有逃逸出 方法的话,那么就可能被优化称栈上分配,这样就无需在堆上分配内存,也无须进行垃圾回收了,这也是最常见的对外存储技术

TaoBaoVM 其中创新的 GCIH(GC invisible heap) 技术实现 off-heap,将生命周期较长的 Java 对象从 heap 中移至 heap外,并且 GC 不能管理 GCIH 内部的 Java 对象,以此达到降低 GC 的回收频率和提升 GC 的回收效率的目的

逃逸分析概述:

  • 如何将堆上的对象分配到栈,需要使用逃逸分析手段
  • 这时一种可以有效减少 Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法
  • 通过逃逸分析, Java Hotspot 编译期能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上
  • 逃逸分析的基本行为就是分析对象动态作用域: 当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸。当一个对象在方法中被定义后,它被外部方法所引用,则认为发生逃逸。
/**
 *  new 的对象实体是否有可能在方法外被调用
 */
public class EscapeAnalysis {
    public EscapeAnalysis obj;

    /**
     * 方法返回 EscapeAnalysis 对象,发生逃逸
     * @return
     */
    public EscapeAnalysis getInstance() {
        return obj == null? new EscapeAnalysis() : obj;
    }

    /**
     * 为成员属性,发生逃逸, obj 声明为 static 仍然发生逃逸
     */
    public void setObj(){
        this.obj = new EscapeAnalysis();
    }

    /**
     * 对象的作用域在当前方法中有效,没有发生逃逸
     */
    public void useEscapeAnalysis() {
        EscapeAnalysis e = new EscapeAnalysis();
    }

    /**
     * 引用成员变量的值,发生逃逸
     */
    public void useEscapeAnalysis1() {
        EscapeAnalysis e = getInstance();
    }
}

参数设置: 

  • 在 JDK 6u23 版本之后,HotSpot 中摩尔呢就已经开启了逃逸分析
  • 如果较早版本,选项 " -XX:+DoEscapeAnalysis " 显式开启逃逸分析。通过选项 "-XX:+PrintEscapeAnalysis" 查看逃逸分析的筛选结果

逃逸分析:代码优化

  • 栈上分配: 将堆分配转换为栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会逃逸,对象可能是栈分配的候选,而不是堆分配
  • 同步省略: 如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步
  • 分离对象或标量替换: 有的对象可能不需要作为一个连续的内存结构存放也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在 CPU 的寄存器中

栈上分配:

  • JIT 编译器在编译期间根据逃逸分析的结果,发现如果一个对象并没有逃逸出方法,就可能被优化成栈上分配。分配完成后,继续在调用栈内执行,最后线程结束,栈空间被回收,局部变量对象也被回收。这样就无须进行垃圾回收了
  • 常见场景: 成员变量赋值,方法返回值,实例引用传递
/**
 * -Xmx1G -Xms1G --XX:-DoEscapeAnalysis -XX:+PrintGCDetails
 */
public class StackAllocation {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        for (int i = 0; i < 1000000; i++) {
            alloc();
        }
        long end = System.currentTimeMillis();

        System.out.println("花费的时间为: " + (end - start) + " ms");

        try{
            Thread.sleep(1000000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    private static void alloc(){
        User user = new User();
    }

    static class User{}
}

同步省略:

  • 线程同步的代价是相当高的,同步的后果是降低并发性和性能
  • 在动态编译同步块的时候,JIT 编译期可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。如果没有,那么JIT 编译期在编译同步块的时候就会取消这部分代码的同步。这样就能大大提高并发性和性能。这个取消同步的过程就是同步省略,也叫锁消除
public class SynOmitTest {
    public void f(){
        Object hillis = new Object();
        synchronized (hillis){
            System.out.println(hillis);
        }
    }

    /**
     * JIT 优化后
     */
    public void omitF(){
        Object hillis = new Object();
        System.out.println(hillis);
    }

}

分离对象或标量替换:

  • 是指一个无法再分解成更小的数据的数据。 Java 中的原始数据类型就是标量
  • 可以分解的数据叫做聚合量(Aggregate),Java 中的对象就是聚合量,因为他们可以分解成其他聚合量和标量
  • 在 JIT 阶段,如果经过逃逸分析,发现一个对象不会被外界访问的话,那么经过 JIT 优化,就会把这个对象拆解成若干个其中包含的若干个成员变量来替换。这个过程就是标量替换
  • 参数 -XX:+EliminateAllocations: 开启标量替换(默认打开),允许将对象打散分散在栈上
public class ScalarTest {
    static class Point{
        private int x;
        private int y;

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

    private static void alloc(){
        Point point = new Point(1,2);
        System.out.println("point.x=" + point.x + "; point.y=" + point.y);
    }

    /**
     * JIT 分析后
     */
    private static void JITAlloc(){
        int x = 1;
        int y = 2;
        System.out.println("point.x=" + x + "; point.y=" + y);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/19930.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

airserver7.2.7最新中文版下载及功能介绍

最近开会打算把手机投屏到自己的Mac上演示用&#xff0c;于是就打算用下听了很久好用但是一值没有使用的AirServer!十分简单的操作就可以完美的投屏到Mac电脑&#xff0c;而且不用像Mac自带的QuickTime用线连接手机!它可以把AirPlay / AirTunes上的音频、视频、照片、幻灯片还有…

对称算法模式-GCM(Galois/Counter Mode)

以下内容来自《NIST Special Publication 800-38D November, 2007》- Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC。 链接在此 AES Galois/Counter Mode 1. 加密步骤 2. 解密步骤 3. GCTR函数 4. GHASH函数 5. 块数据乘法 6. C…

大势智慧软硬件技术答疑第三期

1.重建大师6.0试用版&#xff0c;怎么导出DOM、DEM&#xff1f; 答&#xff1a;需要先生成三维模型&#xff0c;然后再提交产品选择DOM和DEM。 2.麻烦问下&#xff0c;修模出来贴的纹理图片&#xff0c;导出osgb后再打开就模糊了是什么情况&#xff1f; 答&#xff1a;拿高清…

【学习笔记-myabtis】使用mybtis对接pgsql的postgis插件,获取地理字段Geometry信息

使用mybtis对接pgsql的postgis插件&#xff0c;获取地理字段geometry信息 参考资料&#xff1a; Mybatis 自定义TypeHandler - 邓维-java - 博客园 1、如何使用typehandler ​ 相信大家用Mybatis这个框架至少一年以上了吧&#xff0c;有没有思考过这样一个问题&#xff1a;数据…

Hello, Mojo——首次体验Mojo语言

Hello, Mojo——首次体验Mojo语言 文章目录 Hello, Mojo——首次体验Mojo语言一、前言二、Mojo有哪些独特的功能使它不同于Python&#xff1f;三、可以在 Mojo 中导入和使用的 Python 哪些包&#xff1f;四、为什么参数化在 Mojo 中对于使用 SIMD 类型和硬件加速器很重要&#…

[Orillusion]-使用 -windwos-4行命令

前两天看了webgpu的开源库Orillusion | 专业 WebGPU 引擎 Orillusion感觉很不错的样子&#xff0c;准备试一下。因为都是做OpenGL和windows桌面端。 web有点小陌生&#xff0c;记录一下。 准备&#xff1a; Google Chrome Canary 最新版&#xff0c;老版本有问题 nodejs 版…

k8s架构了解

Kubernetes(k8s)是用于自动部署、扩展和管理“容器化应用程序”的开源系统 k8s由control plane以及cluster nodes构成 control plane control plane是维护所有k8s对象记录的系统&#xff0c;持续管理着对象状态&#xff0c;并且对集群的变化做出响应&#xff0c;并使状态匹…

matlab实验三程序设计与优化

学聪明点&#xff0c;自己改&#xff0c;别把我卖了 一、实验目的及要求 一、实验的目的与要求 1、掌握 MATLAB的函数 2、掌握 MATLAB的程序流 3、掌握 MATLAB脚本和函数文件的编写 4、熟悉基于矩阵的程序设计与优化 二、实验原理 1、MATLAB的M文件&#xff1a;脚本文件与函数…

MMM(Master-Master replication manager for MySQL)

MMM&#xff08;Master-Master replication manager for MySQL&#xff0c;MySQL主主复制管理器&#xff09; 是一套支持双主故障切换和双主日常管理的脚本程序。MMM 使用 Perl 语言开发&#xff0c;主要用来监控和管理 MySQL Master-Master &#xff08;双主&#xff09;复制&…

matlabR2021b启动很慢和初始化时间很长解决

工具&#xff1a;MatlabR2021b。 问题记录&#xff0c;在网上下载安装包后&#xff0c;安装后&#xff0c;发现软件启动时间很长。进入界面后软件需要较长时间的初始化。才能就绪。 查询原因为软件需要在启动是查询licence。 首先在安装文件夹中启动Activate MATLAB R2021b。…

图解LeetCode——240. 搜索二维矩阵 II

一、题目 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。 每列的元素从上到下升序排列。 二、示例 2.1> 示例 1&#xff1a; 【输入】matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,1…

leaflet根据坐标点设置多边形,生成geojson文件,计算面积值(133)

第133个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中根据坐标点设置多边形,通过.toGeoJSON() 来生成geojson文件,通过turf.area来计算面积值。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共123…

Java基础之ConcurrentHashMap答非所问

ConcurrentHashMap的数据结构是什么&#xff1f; ConcurrentHashMap仅仅是HashMap的线程安全版本&#xff0c;针对HashMap的线程安全优化&#xff0c;所以HashMap有的特点ConcurrentHashMap同意具有&#xff0c; ConcurrentHashMap的数据结构跟HashMap是一样的。 在JDK7版本使用…

QTableView编程——Model/View架构(单元格随意拖拽交换)

QTableView编程——Model/View架构 基础知识 添加表头 //准备数据模型QStandardItemModel *student_model new QStandardItemModel();student_model->setHorizontalHeaderItem(0, new QStandardItem(QObject::tr("Name")));student_model->setHorizontalHea…

Python Scrapy爬虫框架安装和创建

1、检查Win环境 python版本 python 2、whl方式安装 twisted twisted异步网络框架&#xff0c;可加快下载速度。优点是用少量的代码实现快速的抓取。 由于scrapy需要twisted的环境&#xff0c;我们直接去下载whl文件根据自己的Python版本选择 https://www.lfd.uci.edu/~gohlke/p…

STM32F103ZET6驱动TOF250激光测距传感器

STM32驱动TOF250激光测距传感器 TOF250介绍I2C通讯协议I2C寄存器地址 TOF250引脚说明和STM32的接线和STM32的接线 程序实验结果总结 TOF250介绍 TOF250是一款基于TOF原理的单点测距雷达&#xff0c;采用940nm红外光源&#xff0c;提供了精确和可重复的远 距离测量用于高速自动对…

集群时间同步

集群时间同步 时间同步的方式&#xff1a;找一个机器&#xff0c;作为时间服务器&#xff0c;所有的机器与这台集群时间进行定时的同步&#xff0c;比如&#xff0c;每隔十分钟&#xff0c;同步一次时间。 1.配置时间同步具体实操&#xff1a; 1.1&#xff09;时间服务器配…

有没有中国版的chatGPT?

ChatGPT是一个基于人工智能的聊天机器人&#xff0c;它可以通过自然语言处理技术与用户进行交互和对话。ChatGPT的目的是为用户提供便捷的问答服务和娱乐&#xff0c;它可以回答各种问题&#xff0c;例如天气、新闻、历史、文化、科技、娱乐等等。ChatGPT的核心技术是基于GPT模…

银河麒麟系统Arm64编译opencv指南

进入opencv官网下载版本&#xff1b;我这边下载的是2.4.13.6 &#xff1b;根据需要下载最新的 Releases - OpenCV 拷贝进麒麟系统我这边是麒麟V10 sp1 2204&#xff1b;并解 cmake 在麒麟应用商城中安装&#xff1b; 打开cmake 设置opencv路径&#xff1b;builder文件夹可以自…

内卷时代,大厂产品经理仅用3步破局

本文首发自「慕课网」&#xff0c;想了解更多IT干货内容&#xff0c;程序员圈内热闻&#xff0c;欢迎关注"慕课网"&#xff01; 作者&#xff1a;申悦|慕课网讲师 在当下互联网环境下&#xff0c;产品经理究竟要如何破局&#xff1f; 我认为&#xff0c;既然要破局…