性能优化-内存泄漏、内存溢出、cpu占用高、死锁、栈溢出、FullGC频繁检测手段-总结与分享

news2024/11/18 10:53:34

介绍

什么是内存泄漏

含义:内层泄露是程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费。(换言之,GC回收不了这些不再被使用的对象,这些对象的生命周期太长) 危害:当应用程序长时间连续运行时,会导致严重的性能下降;OOM;偶尔会耗尽连接对象;可能导致频繁GC。(大量Full GC发生也可推测系统可能发生内存溢出)

什么是内存溢出

含义:内层溢出通俗理解就是内存不够,程序要求的内存超出了系统所能分配的范围。 危害:内存溢出错误会导致处理数据的任务失败,甚至会引发平台崩溃等严重后果。

什么是CPU飙升

应用程序CPU使用率高,甚至超过100%

什么是死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

什么是栈溢出

Java 里的 StackOverflowError。抛出这个错误表明应用程序因为深递归导致栈被耗尽了。每当java程序启动一个新的线程时,java虚拟机会为他分配一个栈,java栈以帧为单位保持线程运行状态;当线程调用一个方法是,jvm压入一个新的栈帧到这个线程的栈中,只要这个方法还没返回,这个栈帧就存在。 如果方法的嵌套调用层次太多(如递归调用),随着java栈中的帧的增多,最终导致这个线程的栈中的所有栈帧的大小的总和大于-Xss设置的值,而产生StackOverflowError溢出异常。

内存泄漏、内存溢出、CPU飙升三者之间的关系

内存泄露可能会导致内存溢出。 内存溢出会抛出异常,内存泄露不会抛出异常,大多数时候程序看起来是正常运行的。 内存泄露的程序,JVM频繁进行FullGC尝试释放内存空间,进而会导致CPU飙升 内存泄露过多,造成可回收内存不足,程序申请内存失败,结果就是内存溢出。

基本命令

首先了解各个基本命令、工具的使用,用它们去分析JVM参数,后文案例均是基于以下命令/工具解决。

top free df jps

# 先掌控全局,分别获取执行中的程序进程情况、显示内存的使用情况、查看磁盘剩余空间
top free df 

# 获取java进程的PID
jps 或者ps -ef|grep java

jinfo

可以打印一些当前jvm的各种参数,比如jvm的一些启动参数,jvm中的一些属性k-v等。

jinfo [option] pid

jmap(内存溢出解决方案)

这个命令可以查看JVM内存的一些相关数据

  1. 堆历史:可以看到当前JVM中所有已加载内的类创建对象的数量,占用内存等,可以导入文件中查看;
jmap -histo[:live] <pid> [ > ./xx.log]
  1. 堆信息:可以查看java程序新生代和老年代的占比即使用情况。
jmap -heap <pid>
  1. 堆转储:可以dump堆日志(保存堆现场),再使用visualVM查看jmap生成的堆转储快照。
jmap -dump:live,format=b,file=heap.hprof <pid>

3.1 HeapDump文件 HeapDump 文件是一个二进制文件,它保存了某一时刻JVM堆中对象使用情况(指定时刻Java堆栈的快照),是一种镜像文件。jhat可分析heapdump文件,但是jhat命令在JDK9、JDK10中已经被删除,官方建议用VisualVM代替。 自动导出dump文件:通过JVM参数HeapDumpOnOutOfMemoryError,可以让JVM在出现内存溢出时候Dump出当前的内存转储快照。

# 在IDE中VM option中添加了以下环境变量,程序OOM后生成文件,后缀名为hprof
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./

3.2 VisualVM工具

VisualVM 能够监控线程状态、内存使用情况、CPU 使用情况

jstack(cpu占用高解决方案)

这个命令可以查看线程的堆栈信息,定位到简单的死锁,常用的是通过jstack定位CPU高的问题,具体步骤是:

  1. 查看当前占用cpu最高的进程pid(COMMAND列);
top
  1. 获取当前进程中所有线程占CPU的情况(也可top -p再按 H);
top -Hp <pid>
  1. 将占用最高的tid转换为16进制
printf "%x\n" <tid>;
  1. 查看占用最高的线程的堆栈状态。通过这个流程可以直接定位到哪个线程正在执行占用了大量的cpu。其中A10 就是过滤到关键词之后(A:after)10行信息。
jstack <pid> | grep -A10 <16进制tid>
  1. 前面的步骤已经获取了堆栈信息,我们也可以保存线程栈现场到指定文件里分析。
jstack <pid> > jstack.log 

jstat(FullGC频繁解决方案)

这个命令可以查看堆的各个部分的详细的使用情况,可以通过jstat --help查看帮助;

jstat -gc <pid> [1000 10]

查看gc情况,每1秒打印一次总共打印10次(可选),可以查看各个带的使用总大小和使用大小对于jvm的优化就是要去优化它的FullGC次数,FullGC越少越好,最好控制在FullGC几个小时甚至几天一次,具体看业务的情况。

jstat参数说明:

S0C:第一个幸存区的大小(From Survivor区),以下几个容量的单位都是KB 
S1C:第二个幸存区的大小 (To Survivor区)
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小 
EC:伊甸园区的大小 (Eden区)
EU:伊甸园区的使用大小
OC:老年代大小 
OU:老年代使用大小 
MC:方法区大小(元空间)
MU:方法区使用大小 
CCSC:压缩类空间大小 
CCSU:压缩类空间使用大小 
YGC:年轻代垃圾回收次数 
YGCT:年轻代垃圾回收消耗时间,单位s 
FGC:老年代垃圾回收次数 
FGCT:老年代垃圾回收消耗时间,单位s 
GCT:垃圾回收消耗总时间,单位s

根据jstat查看出来的gc情况,我们可能需要以下几个主要指标:

各内存区域大小是否合理;
观察Eden区的对象增长,如每秒有多少对象创建;
每次YoungGC后有多少对象存活下来、有多少对象进入了老年代;
YoungGC的耗时;
FullGC触发频率及耗时;

GC分析

  • Minor GC/Young GC: 指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快。
  • Major GC/Full GC: 一般会回收老年代 ,年轻代,方法区的垃圾,Major GC的速度一般会比Minor GC的慢 10倍以上。

大量的对象在Eden区分配,YoungGC之后存活的对象经过S0、S1,大对象与长期存活的对象可能会到Old区。Eden与Survivor区默认8:1:1。

  • 触发YoungGC时机: Eden区域满了
  • 触发FullGC时机:

1.System.gc() 显式触发Full GC

2.老年代空间不足,晋升到老年代的对象大小大于老年代的可用内存。进入到老年代有多种情况:

1)Survivor区的对象满足晋升到老年代的条件,即对象年龄达到了MaxTenuringThreshold,这是一般情况;

2)根据对象动态年龄判断机制:在YoungGC后判断,Survivor区中年龄 1 到 N 的对象大小是否超过 Survivor 的 50% ,这会让大于等于年龄 N 的对象放入老年代(-XX:TargetSurvivorRatio),如果此时老年代没有足够的空间来放置这些对象也会引起Full GC;

3)堆中产生大对象超过阈值(-XX:PretenureSizeThreshold):很长的字符串或者数组在被创建后会直接进入老年代

3.元空间空间不足(-XX:MetaspaceSize)

4.老年代空间分配担保失败:在YoungGC前判断,YoungGC后晋升到Old区的历史平均大小是否大于本次Old区剩余空间大小(XX:-HandlePromotionFailure)

更多C++后台开发技术点知识内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,音视频开发,Linux内核,TCP/IP,协程,DPDK多个高级知识点。

C/C++Linux服务器开发高级架构师/C++后台开发架构师​免费学习地址

【文章福利】另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以点击领取

Arthas

官方文档:https://arthas.aliyun.com

使用略

  • 命令合并

下文部分命令是多个命令的合并,与拆开输入等效

# 例如,当程序名为StaticTest时
java -jar arthas-boot.jar  `ps -ef|grep StaticTest |grep -v grep|awk '{print $2}'`
jstat -gc `jps|grep StaticTest |grep -v grep|awk '{print $1}'` 500 1000
jvisualvm --openpid `ps -ef|grep StaticTest |grep -v grep|awk '{print $2}'`

内存泄漏案例分析

介绍

JAVA在内存管理上有着独一无二的优势,它取消了指针,引入垃圾回收机制,由垃圾收集器(GC)来自动管理内存回收;GC隐式地负责分配和释放内存,因此能够处理大多数内存泄漏问题。虽然GC有效地处理了相当一部分内存,但它不能保证对内存泄漏提供万无一失的解决方案。即使在认真的开发人员的应用程序中,内存泄漏仍然可能悄悄发生。所以我们有必要了解内存泄漏的潜在原因是什么,如何在运行时识别它们,以及如何在应用程序中处理它们。

案例一、通过静态字段的内存泄漏

第一种可能导致潜在内存泄漏的情况是大量使用静态变量。静态字段的生命周期与正在运行的应用程序一致。

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

public class StaticTest {
    public static List<Double> list = new ArrayList<>(); //静态集合

    public void populateList() {
        for (int i = 0; i < 10000000; i++) {
            list.add(Math.random());
        }
        System.out.println("Debug Point 2");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(10000);
        System.out.println("Debug Point 1");
        new StaticTest().populateList();
        System.out.println("Debug Point 3");
        Thread.sleep(10000);
        System.gc(); //有修改,在此处显示触发Full GC
        Thread.sleep(Integer.MAX_VALUE);
    }
}

如果我们分析这个程序执行期间的堆内存,那么我们将看到在调试点1和2之间,堆内存如预期的那样增加了。但是当我们在调试点3离开populateList()方法时,堆内存还没有被垃圾回收。

​然而,如果我们在上面的程序删除了关键字static,那么它将给内存使用带来巨大的变化。

静态集合类持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。

#查看jvm默认具体参数
java -XX:+PrintCommandLineFlags -version 
java -XX:+PrintGCDetails -version

比较关键字static带来堆空间大小回收的差异,两者差距接近300M;10000000个Double对象没有被回收,再根据对齐填充8的倍数,反推出来一个Double包装对象占用32字节的空间。

结论:我们需要密切关注静态变量的使用。静态的集合或大型对象在整个应用程序的生命周期中都被保留在内存中,这些可在其他地方使用的重要内存空间就被浪费掉了。

建议:尽量减少静态变量的使用;单例对象懒加载,需要用对象的时候再创建,而不是初始化时就创建好了对象。

案例一变种

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

/**
 * JVM参数默认
 * @author luke
 * @date 2022/11/11
 */
public class StaticTest {
    public static List<Integer> list = new ArrayList<>(100000000);
    public void populateList() throws InterruptedException {
        for (int i = 1; i <= 100000000; i++) {
            list.add(i);
            if(i % 100000 == 0){
                Thread.sleep(1000);
                //System.out.println(list.size());
            }
        }
        System.out.println("running......");
    }
    public static void main(String[] args) throws InterruptedException {
        System.out.println("before......");
        new StaticTest().populateList();
        System.out.println("after......");
    }
}
// 代码最终因内存泄露,回收不了可用空间而OOM。
// Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

先使用jstat命令统计垃圾回收,间隔时间500毫秒打印一次。根据一个Integer对象占用16字节,每1秒钟向list添加100000个整数,16*100000字节大约是1.5M,与统计图eden区平均每秒产生对象的大小接近。 再观察jvisualvm中堆内存的曲线图,每分钟平均生产100M对象,数据相符合。

使用命令如下:

jstat -gc `ps -ef|grep StaticTest |grep -v grep|awk '{print $2}'` 500 1000
jvisualvm --openpid `ps -ef|grep StaticTest |grep -v grep|awk '{print $2}'`

案例二、连接资源未关闭

import java.io.File;
import java.io.IOException;

/**
 * @author luke
 * @date 2022/10/27
 */
public class FileTest {
    public static void main(String[] args) throws IOException {
        File f = new File("C:\\Users\\lzyxx\\Desktop\\a.txt");
        System.out.println(f.exists());
        System.out.println(f.isDirectory());
    }
}

各种连接,如数据库连接、网络连接和IO连接等,如果不显性地关闭连接资源,将会造成大量的对象无法被回收,从而引起内存泄漏。

案例三、equals()和hashCode()方法使用不当

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 线程池通过submit方式提交任务,会把Runnable封装成FutureTask。
 * 直接导致了Runnable重写的toString方法在afterExecute统计的时候没有起到我们想要的作用(重写toString以用于统计任务数),
 * 最终导致几乎每一个任务(除非hashCode相同)就按照一类任务进行统计。所以这个metricsMap会越来越大,调用metrics接口的时候,会把该map转成一个字符返回。
 * 改成execute方式提交任务即可
 */
public class GCTest {
    /**
     * 统计各类任务已经执行的数量, 此处为了简化代码,只用map来代替metrics统计
     */
    private static final Map<String, AtomicInteger> metricsMap = new ConcurrentHashMap<>();

    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>()) {
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                super.afterExecute(r, t);
                metricsMap.compute(r.toString(), (s, atomicInteger) -> new AtomicInteger(atomicInteger == null ? 1 : atomicInteger.incrementAndGet()));
            }
        };
        /**
         * 线程池执行两类任务
         */
        for (int i = 0; i < 500; i++) {
            executor.submit(new SimpleRunnable()); // 错误方式
            executor.submit(new SimpleRunnable2());
//            executor.execute(new SimpleRunnable()); // 正确方式
//            executor.execute(new SimpleRunnable2());
        }
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.DAYS);
        System.out.println(metricsMap);
    }
    static class SimpleRunnable implements Runnable{
        @Override
        public void run() {}
        @Override
        public String toString(){
            return this.getClass().getSimpleName();
        }
    }

    static class SimpleRunnable2 implements Runnable{
        @Override
        public void run() {}
        @Override
        public String toString(){
            return this.getClass().getSimpleName();
        }
    }
}

案例四、ThreadLocal的错误使用

如果任何类创建了ThreadLocal变量,但没有显式删除它,那么即使在web应用程序停止后,该对象的副本也将保留在工作线程中,从而防止对象被垃圾收集。

案例:内存的最大大小为1m,while循环每隔100ms申请30kb大小的空间

import java.lang.reflect.Field;

/**
 * jvm运行参数 -Xmx1m -Xms1m -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./
 *
 * 使用以下命令观察:jstat -gc `ps -ef|grep MemoryLeakExample |grep -v grep|awk '{print $2}'` 1000 1000
 */
public class MemoryLeakExample{

    public static int i = 0;

    public static void main(String[] args) throws Exception{
        while (true){
            ThreadLocal<Object> threadLocal = new ThreadLocal<>();
            try {
                objectThreadLocal.set(new byte[10 * 1024]);
                printEntriesSize();
                Thread.sleep(100);
                i++;
            }catch (Throwable e){
                System.out.println(i);
                throw e;
            }finally {
                //threadLocal.remove(); // 正确使用
            }
        }
    }

    /**
     * 打印ThreadLocal.entry的个数
     */
    public static void printEntriesSize() throws NoSuchFieldException, IllegalAccessException{
        Thread thread = Thread.currentThread();
        Class<? extends Thread> aClass = thread.getClass();
        Field threadLocals = aClass.getDeclaredField("threadLocals");
        threadLocals.setAccessible(true);
        Object threadLocalMap  = threadLocals.get(thread);
        Class<?> tlmClass = threadLocalMap.getClass();
        Field entriesSize = tlmClass.getDeclaredField("size");
        entriesSize.setAccessible(true);
        System.out.println(entriesSize.get(threadLocalMap));
    }
}

为什么程序没有因可用空间越来越少而oom,ThreadLocal.entry的个数会周期性的由少变多? 解释:ThreadLocalMap底层使用数组来保存元素,利用线性探测法解决哈希冲突,但是调用ThreadLocal#set,遍历Entry数组过程中会清理key为null的value,尽量保证不出现内存泄漏的情况。

  • 如何预防?
  1. 当我们不再使用ThreadLocal时,记得清理它们。ThreadLocals提供了remove()方法,该方法将删除此变量的当前线程值。
  2. 不要使用ThreadLocal#set(null)以清除该值。它实际上不会清除该值,而是会查找与当前线程关联的Map,并将键值对分别设置为当前线程和null。
  3. 最好将ThreadLocal 视为需要在finally块中关闭的资源。
try {
    threadLocal.set(System.nanoTime());
    //... further processing
} finally {
    threadLocal.remove();
}

案例五、缓存泄漏

内存泄漏的另一个常见来源是缓存,一旦你把对象引用放入到缓存中,就很容易遗忘。

  • 如何预防?

使用WeakHashMap 缓存对象,这个map 的特点是当除了自身有对key的引用外,此key没有其他引用那么此map会自动丢弃此值

案例六、 内部类持有外部类

嵌套类分为两类:非静态类和静态类。非静态嵌套类称为内部类。声明为静态的嵌套类称为静态内部类。 按照嵌嵌套类的语法限定,非静态内部类(InnerClass)可以访问其封闭类(OuterClass)的成员,即使这些成员是私有的。而静态内部类没有权限访问OuterClass的成员(当然静态类成员除外)。

默认情况下,每个非静态内部类(InnerClass)都有对其包含类(OuterClass)的隐式引用。如果我们在应用程序中使用这个内部类对象,那么即使在我们的包含类对象超出范围之后,它也不会被垃圾收集。

  • 如何预防?

如果内部类不需要访问包含的类成员,请考虑将其转换为静态类。

public class OuterClass {
    class InnerClass {
    }
    static class StaticClass {
    }
}

cpu占用高案例分析

CPU占用飙升甚至超过100%的原因分析:

  1. 内存消耗过大,导致Full GC次数过多

多个线程的CPU都超过了100%,通过jstack命令可以看到这些线程主要是垃圾回收线程(VM Thread); 通过jstat命令监控GC情况,可以看到Full GC次数非常多,并且次数在不断增加。

  1. 代码中有大量消耗CPU的操作,导致CPU过高,系统运行缓慢

例如某些复杂算法,甚至算法BUG,无限循环递归等等。jstack命令可直接定位到代码行。

  1. 由于锁使用不当,导致死锁

死锁不会直接导致 cpu 资源占用过高,synchronize 和 AQS中锁的设计是线程获取锁失败时,会主动挂起线程,而不会自旋循环检测锁是否被释放。 如果因为死锁,阻塞线程越来越多,内存占用也越来越高且无法释放,导致不停的 gc,会造成CPU占用飙升。

  1. 线程由于某种原因而进入TIMED_WAITING、WAITING状态

使用 synchronized 会让等待锁的线程处于 Blocked 状态;

使用 AQS 相关的锁则会让等待锁的线程处于 TIMED_WAITING、 WAITING 状态,因为底层基于 LockSupport;

内存溢出案例分析

一般来说内存溢出主要分为以下几类:

  • 堆溢出(java.lang.OutOfMemoryError: Java heap space) 最常见最复杂情况
  • 栈深度不够( java.lang.StackOverflowError) 需关注配置项 -Xss大小
  • 栈线程数不够(java.lang.OutOfMemoryError: unable to create new native thread)
  • 元空间溢出(java.lang.OutOfMemoryError: Metaspace) 需关注配置项 -XX:MaxMetaspaceSize大小、jstat 指标参数 MC、MU 如果发现元空间大小是持续上涨的,则需要检查代码是否存在大量的反射类加载、动态代理生成的类加载等导致。可以通过-XX:+TraceClassLoading -XX:+TraceClassUnloading记录下类的加载和卸载情况,反推具体问题代码。

原文链接:性能优化-内存泄漏、内存溢出、cpu占用高、死锁、栈溢出、FullGC频繁检测手段-总结与分享 - Luke! - 博客园

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux学习03-Linux基础命令1

1 开始执行命令 command [-options] parameter1 parameter2 一行命令中第一个输入的部分绝对是命令或者可执行文件。 例如 ls命令列出目录的文件&#xff0c;需要列出隐藏文件则加 -al。 ls -al ls ls -a -l 2 基础命令操作 显示日期与时间的命令&#xff1a;date 显示日…

Ubuntu22.04系统中二进制包安装公有云k8s

目录公有云版k8s的架构是怎样的公有云中创建k8s实例的过程如下二进制法创建k8s的一般过程Kubernetes的重要性check nodes每台服务器执行基线配置CA rootetcd HA cluster根据CA根证书创建etcd的专有CA证书将etcd注册成为systemd服务配置各master节点的etcd.confansible配置各个m…

东北大学2023分布式操作系统考试重点

Note&#xff1a;以下10个点为老师画的重点 1.分布式系统目标 第一章 分布式系统概论&#xff0c;第二节 2.RPC过程 第四章 分布式通信管理&#xff0c; 第二节 客户过程以普通方式调用相应的客户存根客户存根建立消息并激活内核陷阱内核将消息发送到远程内核远程内核将消息…

计算机毕设Python+Vue写字楼物业管理系统(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

C++ Reference: Standard C++ Library reference: Containers: map: map: operator[]

C官网参考链接&#xff1a;https://cplusplus.com/reference/map/map/operator[]/ 公有成员函数 <map> std::map::operator[] C98 mapped_type& operator[] (const key_type& k); C11 mapped_type& operator[] (const key_type& k); mapped_type& …

SQL基础——数据更新

数据更新前言思维导图数据的插入&#xff08;INSERT语句的使用方法&#xff09;什么是INSERTINSERT语句的基本语法语法4.1 INSERT语句代码示例4.2 创建 ProductIns表的CREATE TABLE语句代码示例4.3 向表中插入一行数据代码示例4.4 INSERT插入多行数据列清单的省略代码示例4.5 省…

chatGPT的49种应用场景,及各开发语言对接chatGPT参考指南

前沿 可能有人在问我&#xff0c;勇哥为什么chatGPT都被微信下架了&#xff0c;你还要写相关的chatGPT的文章呢&#xff1f;其实我们先不论微信下架的原因&#xff0c;单说chatGPT的达芬奇模型给勇哥带来的科技感早就超越了一切&#xff0c;所以勇哥依旧决定连续熬两个夜为大家…

【与达梦同行】达梦驱动图谱

达梦驱动图谱 摘要 达梦提供了大部分主流开发语言的驱动接口&#xff0c;在我用使用过的国产数据库中对客户端驱动的支持应该算是非常不错的。本文主要介绍达梦的驱动开发&#xff0c;通过实际操作&#xff0c;从环境搭建到实践验证&#xff0c;介绍了达梦各种语言驱动的详细使…

博途S7-1500T 使用工艺对象驱动液压轴(含SimaHydTO库)

利用工艺对象控制液压轴位置含PLC控制和仿真程序文档资料下载地址如下: https://support.industry.siemens.com/cs/document/109756217/simatic-s7-1500(t)-lsimahydto-for-hydraulic-applications-and-hydraulic-presses?dti=0&dl=en&lc=zh-CNhttps://support.indu…

圣诞树-python绘制雪夜圣诞树并封装为小程序

绘制雪夜圣诞树并封装为小程序 使用turtle绘制一颗雪夜圣诞树&#xff0c;然后封装成exe小程序送给你的朋友吧&#xff01; PS&#xff1a;只能在windows运行。 转载注明本文链接和作者 先看效果图&#xff1a; 绘制雪夜圣诞树 由于代码有三百多行&#xff0c;我放在下面的两…

一文彻底搞懂cookie、session、token

1.Cookie Cookie是客户端保存用户信息的一种机制&#xff0c;用来记录用户的一些信息&#xff0c;实际上Cookie是服务器在本地机器上存储的一小段文本&#xff0c;并随着每次请求发送到服务器。 Cookie技术通过请求和响应报文中写入Cookie信息来控制客户端的状态。 Cookie会…

28.项目搭建网关

项目搭建网关 一、项目架构 二、创建模块&#xff0c;引入依赖&#xff0c;创建启动类&#xff0c;添加application.yml配置文件 2.1依赖 <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-…

【AI with ML】第 9 章 :了解序列和时间序列数据

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

【linux】linux环境变量初始化与对应文件的生效顺序

1.认识环境变量&#xff08;PATH) 环境变量&#xff08;PATH&#xff09;是一个列表&#xff0c;里面是有序的一些程序的地址&#xff08;一般是xxx/bin&#xff09;&#xff0c;添加环境变量后&#xff0c;系统在任何目录输入命令&#xff0c;都可以识别命令并执行命令对应程…

一个工程师的自我修养!

Datawhale干货 Tw93&#xff0c;前端工程师&#xff0c;Datawhale邀约作者对计算机科学的掌握程度&#xff0c;决定了一个计算机行业从业者能走多远。吴军博士在《浪潮之巅&#xff08;第四版&#xff09;》对工程师做了五个级别的划分&#xff1a;一级&#xff1a;能够开创一个…

询问ChatGPT来了解什么是边缘计算,有哪些特点,有哪些关键技术,和云计算的关系是什么?

什么是云计算 云计算是一种计算架构&#xff0c;它使用互联网作为基础设施&#xff0c;允许用户通过网络访问共享的计算资源&#xff0c;而不需要拥有专门的本地计算机硬件和软件。云计算提供了计算能力、存储空间、应用程序和服务等&#xff0c;并使用计费模型收取使用费用。…

springboot基于ssm框架实现的家具商城管理系统

一、项目简介 本项目是一套基于springboot框架实现的家具商城管理系统&#xff0c;主要针对计算机相关专业的正在做bishe的学生和需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目可以直接作为bishe使用。 项目都经过严格调试&#…

为什么在SPI通信中提供不同的模式?

SPI传输模式 因为SPI是全双工也就是说&#xff08;MOSI和MISO&#xff09;上同时发送和接收数据 时钟极性&#xff08;CPOL&#xff09; 时钟相位&#xff08;CPHA&#xff09;是定义SPI总线使用的时钟格式得主要参数&#xff0c;根据SPOL参数&#xff0c;SPI时钟可以是反向或…

寻找两个正序数组的中位数K个一组翻转链表

文章目录1. [寻找两个正序数组的中位数](https://leetcode.cn/problems/median-of-two-sorted-arrays/)2. [K个一组翻转链表](https://leetcode.cn/problems/reverse-nodes-in-k-group/)1. 寻找两个正序数组的中位数 首先&#xff0c; 中位数的作用就是将正序数组的两端进行分…

[内网渗透]—权限维持

Skeleton Key(万能密码) 原理 当拿到域控权限后,使用mimikatz可以注入Skeleon Key,将 Skeleton Key 注入域控制器的 lsass.exe 进程,这样会在域内的所有账号中添加一个 Skeleton Key,而这个key是自己设定的所以可以随时共享访问。 PS:由于注入到lsass.exe进程中,所以每…