JAVA中神奇的双刃剑--Unsafe

 

参考资料:

 

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。

 

  • Java魔法类:sun.misc.Unsafe
  • 在openjdk8下看Unsafe源码

Unsafe介绍

在Oracle的Jdk8无法获取到sun.misc包的源码,想看此包的源码可以直接下载openjdk,包的路径是:

  • openjdk-8u40-src-b25-10_feb_2015\openjdk\jdk\src\share\classes\sun\misc。

当然,不同的openjdk版本的根目录(这里是openjdk-8u40-src-b25-10_feb_2015)不一定相同。sun.misc包含了低级(native硬件级别的原子操作)、不安全的操作集合。

Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。Unsafe类提供了硬件级别的原子操作,提供了一些绕开JVM的更底层功能,由此提高效率。本文的Unsafe类来源于openjdk-8u40-src-b25-10_feb_2015。

Unsafe的使用建议#

建议先看这个知乎帖子第一楼R大的回答:为什么JUC中大量使用了sun.misc.Unsafe 这个类,但官方却不建议开发者使用。

使用Unsafe要注意以下几个问题:

  • 1、Unsafe有可能在未来的Jdk版本移除或者不允许Java应用代码使用,这一点可能导致使用了Unsafe的应用无法运行在高版本的Jdk。
  • 2、Unsafe的不少方法中必须提供原始地址(内存地址)和被替换对象的地址,偏移量要自己计算,一旦出现问题就是JVM崩溃级别的异常,会导致整个JVM实例崩溃,表现为应用程序直接crash掉。
  • 3、Unsafe提供的直接内存访问的方法中使用的内存不受JVM管理(无法被GC),需要手动管理,一旦出现疏忽很有可能成为内存泄漏的源头。

暂时总结出以上三点问题。Unsafe在JUC(java.util.concurrent)包中大量使用(主要是CAS),在netty中方便使用直接内存,还有一些高并发的交易系统为了提高CAS的效率也有可能直接使用到Unsafe。总而言之,Unsafe类是一把双刃剑。

Unsafe详解

Unsafe中一共有82个public native修饰的方法,还有几十个基于这82个public native方法的其他方法。

1     //扩充内存  2     public native long reallocateMemory(long address, long bytes);  3       4     //分配内存  5     public native long allocateMemory(long bytes);  6       7     //释放内存  8     public native void freeMemory(long address);  9       
10     //在给定的内存块中设置值  
11     public native void setMemory(Object o, long offset, long bytes, byte value);  
12       
13     //从一个内存块拷贝到另一个内存块  
14     public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);  
15       
16     //获取值,不管java的访问限制,其他有类似的getInt,getDouble,getLong,getChar等等  
17     public native Object getObject(Object o, long offset);  
18       
19     //设置值,不管java的访问限制,其他有类似的putInt,putDouble,putLong,putChar等等  
20     public native void putObject(Object o, long offset);  
21       
22     //从一个给定的内存地址获取本地指针,如果不是allocateMemory方法的,结果将不确定  
23     public native long getAddress(long address);  
24       
25     //存储一个本地指针到一个给定的内存地址,如果地址不是allocateMemory方法的,结果将不确定  
26     public native void putAddress(long address, long x);  
27       
28     //该方法返回给定field的内存地址偏移量,这个值对于给定的filed是唯一的且是固定不变的  
29     public native long staticFieldOffset(Field f);  
30       
31     //报告一个给定的字段的位置,不管这个字段是private,public还是保护类型,和staticFieldBase结合使用  
32     public native long objectFieldOffset(Field f);  
33       
34     //获取一个给定字段的位置  
35     public native Object staticFieldBase(Field f);  
36       
37     //确保给定class被初始化,这往往需要结合基类的静态域(field)  
38     public native void ensureClassInitialized(Class c);  
39       
40     //可以获取数组第一个元素的偏移地址  
41     public native int arrayBaseOffset(Class arrayClass);  
42       
43     //可以获取数组的转换因子,也就是数组中元素的增量地址。将arrayBaseOffset与arrayIndexScale配合使用, 可以定位数组中每个元素在内存中的位置  
44     public native int arrayIndexScale(Class arrayClass);  
45       
46     //获取本机内存的页数,这个值永远都是2的幂次方  
47     public native int pageSize();  
48       
49     //告诉虚拟机定义了一个没有安全检查的类,默认情况下这个类加载器和保护域来着调用者类  
50     public native Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);  
51       
52     //定义一个类,但是不让它知道类加载器和系统字典  
53     public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches);  
54       
55     //锁定对象,必须是没有被锁的
56     public native void monitorEnter(Object o);  
57       
58     //解锁对象  
59     public native void monitorExit(Object o);  
60       
61     //试图锁定对象,返回true或false是否锁定成功,如果锁定,必须用monitorExit解锁  
62     public native boolean tryMonitorEnter(Object o);  
63       
64     //引发异常,没有通知  
65     public native void throwException(Throwable ee);  
66       
67     //CAS,如果对象偏移量上的值=期待值,更新为x,返回true.否则false.类似的有compareAndSwapInt,compareAndSwapLong,compareAndSwapBoolean,compareAndSwapChar等等。  
68     public final native boolean compareAndSwapObject(Object o, long offset,  Object expected, Object x);  
69       
70     // 该方法获取对象中offset偏移地址对应的整型field的值,支持volatile load语义。类似的方法有getIntVolatile,getBooleanVolatile等等  
71     public native Object getObjectVolatile(Object o, long offset);   
72       
73     //线程调用该方法,线程将一直阻塞直到超时,或者是中断条件出现。  
74     public native void park(boolean isAbsolute, long time);  
75       
76     //终止挂起的线程,恢复正常.java.util.concurrent包中挂起操作都是在LockSupport类实现的,也正是使用这两个方法
77     public native void unpark(Object thread);  
78       
79     //获取系统在不同时间系统的负载情况  
80     public native int getLoadAverage(double[] loadavg, int nelems);  
81       
82     //创建一个类的实例,不需要调用它的构造函数、初使化代码、各种JVM安全检查以及其它的一些底层的东西。即使构造函数是私有,我们也可以通过这个方法创建它的实例,对于单例模式,简直是噩梦,哈哈  
83     public native Object allocateInstance(Class cls) throws InstantiationException;  

类、对象和变量相关方法#

主要包括类的非常规实例化、基于偏移地址获取或者设置变量的值、基于偏移地址获取或者设置数组元素的值等。

getObject#

  • public native Object getObject(Object o, long offset);

通过给定的Java变量获取引用值。这里实际上是获取一个Java对象o中,获取偏移地址为offset的属性的值,此方法可以突破修饰符的抑制,也就是无视private、protected和default修饰符。类似的方法有getInt、getDouble等等。

putObject#

  • public native void putObject(Object o, long offset, Object x);

将引用值存储到给定的Java变量中。这里实际上是设置一个Java对象o中偏移地址为offset的属性的值为x,此方法可以突破修饰符的抑制,也就是无视private、protected和default修饰符。类似的方法有putInt、putDouble等等。

getObjectVolatile#

  • public native Object getObjectVolatile(Object o, long offset);

此方法和上面的getObject功能类似,不过附加了'volatile'加载语义,也就是强制从主存中获取属性值。类似的方法有getIntVolatile、getDoubleVolatile等等。这个方法要求被使用的属性被volatile修饰,否则功能和getObject方法相同。

putObjectVolatile#

  • public native void putObjectVolatile(Object o, long offset, Object x);

此方法和上面的putObject功能类似,不过附加了'volatile'加载语义,也就是设置值的时候强制(JMM会保证获得锁到释放锁之间所有对象的状态更新都会在锁被释放之后)更新到主存,从而保证这些变更对其他线程是可见的。类似的方法有putIntVolatile、putDoubleVolatile等等。这个方法要求被使用的属性被volatile修饰,否则功能和putObject方法相同。

putOrderedObject#

  • public native void putOrderedObject(Object o, long offset, Object x);

设置o对象中offset偏移地址offset对应的Object型field的值为指定值x。这是一个有序或者有延迟的putObjectVolatile方法,并且不保证值的改变被其他线程立即看到。只有在field被volatile修饰并且期望被修改的时候使用才会生效。类似的方法有putOrderedIntputOrderedLong

staticFieldOffset#

  • public native long staticFieldOffset(Field f);

返回给定的静态属性在它的类的存储分配中的位置(偏移地址)。不要在这个偏移量上执行任何类型的算术运算,它只是一个被传递给不安全的堆内存访问器的cookie。注意:这个方法仅仅针对静态属性,使用在非静态属性上会抛异常。下面源码中的方法注释估计有误,staticFieldOffset和objectFieldOffset的注释估计是对调了,为什么会出现这个问题无法考究。

objectFieldOffset#

  • public native long objectFieldOffset(Field f);

返回给定的非静态属性在它的类的存储分配中的位置(偏移地址)。不要在这个偏移量上执行任何类型的算术运算,它只是一个被传递给不安全的堆内存访问器的cookie。注意:这个方法仅仅针对非静态属性,使用在静态属性上会抛异常。

staticFieldBase#

  • public native Object staticFieldBase(Field f);

返回给定的静态属性的位置,配合staticFieldOffset方法使用。实际上,这个方法返回值就是静态属性所在的Class对象的一个内存快照。注释中说到,此方法返回的Object有可能为null,它只是一个'cookie'而不是真实的对象,不要直接使用的它的实例中的获取属性和设置属性的方法,它的作用只是方便调用上面提到的像getInt(Object,long)等等的任意方法。

shouldBeInitialized#

  • public native boolean shouldBeInitialized(Class<?> c);

检测给定的类是否需要初始化。通常需要使用在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)。 此方法当且仅当ensureClassInitialized方法不生效的时候才返回false。

ensureClassInitialized#

  • public native void ensureClassInitialized(Class<?> c);

检测给定的类是否已经初始化。通常需要使用在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)。

arrayBaseOffset#

  • public native int arrayBaseOffset(Class<?> arrayClass);

返回数组类型的第一个元素的偏移地址(基础偏移地址)。如果arrayIndexScale方法返回的比例因子不为0,你可以通过结合基础偏移地址和比例因子访问数组的所有元素。Unsafe中已经初始化了很多类似的常量如ARRAY_BOOLEAN_BASE_OFFSET等。

arrayIndexScale#

  • public native int arrayIndexScale(Class<?> arrayClass);

返回数组类型的比例因子(其实就是数据中元素偏移地址的增量,因为数组中的元素的地址是连续的)。此方法不适用于数组类型为"narrow"类型的数组,"narrow"类型的数组类型使用此方法会返回0(这里narrow应该是狭义的意思,但是具体指哪些类型暂时不明确,笔者查了很多资料也没找到结果)。Unsafe中已经初始化了很多类似的常量如ARRAY_BOOLEAN_INDEX_SCALE等。

defineClass#

  • public native Class<?> defineClass(String name, byte[] b, int off, int len,ClassLoader loader,ProtectionDomain protectionDomain);

告诉JVM定义一个类,返回类实例,此方法会跳过JVM的所有安全检查。默认情况下,ClassLoader(类加载器)和ProtectionDomain(保护域)实例应该来源于调用者。

defineAnonymousClass#

  • public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);

这个方法的使用可以看R大的知乎回答:JVM crashes at libjvm.so,下面截取一点内容解释此方法。

  • 1、VM Anonymous Class可以看作一种模板机制,如果程序要动态生成很多结构相同、只是若干变量不同的类的话,可以先创建出一个包含占位符常量的正常类作为模板,然后利用sun.misc.Unsafe#defineAnonymousClass()方法,传入该类(host class,宿主类或者模板类)以及一个作为"constant pool path"的数组来替换指定的常量为任意值,结果得到的就是一个替换了常量的VM Anonymous Class。
  • 2、VM Anonymous Class从VM的角度看是真正的"没有名字"的,在构造出来之后只能通过Unsafe#defineAnonymousClass()返回出来一个Class实例来进行反射操作。

还有其他几点看以自行阅读。这个方法虽然翻译为"定义匿名类",但是它所定义的类和实际的匿名类有点不相同,因此一般情况下我们不会用到此方法。在Jdk中lambda表达式相关的东西用到它,可以看InnerClassLambdaMetafactory这个类。

allocateInstance#

  • public native Object allocateInstance(Class<?> cls) throws InstantiationException;

通过Class对象创建一个类的实例,不需要调用其构造函数、初始化代码、JVM安全检查等等。同时,它抑制修饰符检测,也就是即使构造器是private修饰的也能通过此方法实例化。

内存管理#

addressSize#

  • public native int addressSize();

获取本地指针的大小(单位是byte),通常值为4或者8。常量ADDRESS_SIZE就是调用此方法。

pageSize#

  • public native int pageSize();

获取本地内存的页数,此值为2的幂次方。

allocateMemory#

  • public native long allocateMemory(long bytes);

分配一块新的本地内存,通过bytes指定内存块的大小(单位是byte),返回新开辟的内存的地址。如果内存块的内容不被初始化,那么它们一般会变成内存垃圾。生成的本机指针永远不会为零,并将对所有值类型进行对齐。可以通过freeMemory方法释放内存块,或者通过reallocateMemory方法调整内存块大小。bytes值为负数或者过大会抛出IllegalArgumentException异常,如果系统拒绝分配内存会抛出OutOfMemoryError异常。

reallocateMemory#

  • public native long reallocateMemory(long address, long bytes);

通过指定的内存地址address重新调整本地内存块的大小,调整后的内存块大小通过bytes指定(单位为byte)。可以通过freeMemory方法释放内存块,或者通过reallocateMemory方法调整内存块大小。bytes值为负数或者过大会抛出IllegalArgumentException异常,如果系统拒绝分配内存会抛出OutOfMemoryError异常。

setMemory#

  • public native void setMemory(Object o, long offset, long bytes, byte value);

将给定内存块中的所有字节设置为固定值(通常是0)。内存块的地址由对象引用o和偏移地址共同决定,如果对象引用o为null,offset就是绝对地址。第三个参数就是内存块的大小,如果使用allocateMemory进行内存开辟的话,这里的值应该和allocateMemory的参数一致。value就是设置的固定值,一般为0(这里可以参考netty的DirectByteBuffer)。一般而言,o为null,所有有个重载方法是public native void setMemory(long offset, long bytes, byte value);,等效于setMemory(null, long offset, long bytes, byte value);

多线程同步#

主要包括监视器锁定、解锁以及CAS相关的方法。

monitorEnter#

  • public native void monitorEnter(Object o);

锁定对象,必须通过monitorExit方法才能解锁。此方法经过实验是可以重入的,也就是可以多次调用,然后通过多次调用monitorExit进行解锁。

monitorExit#

  • public native void monitorExit(Object o);

解锁对象,前提是对象必须已经调用monitorEnter进行加锁,否则抛出IllegalMonitorStateException异常。

tryMonitorEnter#

  • public native boolean tryMonitorEnter(Object o);

尝试锁定对象,如果加锁成功返回true,否则返回false。必须通过monitorExit方法才能解锁。

compareAndSwapObject#

  • public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x);

针对Object对象进行CAS操作。即是对应Java变量引用o,原子性地更新o中偏移地址为offset的属性的值为x,当且仅的偏移地址为offset的属性的当前值为expected才会更新成功返回true,否则返回false。

  • o:目标Java变量引用。
  • offset:目标Java变量中的目标属性的偏移地址。
  • expected:目标Java变量中的目标属性的期望的当前值。
  • x:目标Java变量中的目标属性的目标更新值。

类似的方法有compareAndSwapIntcompareAndSwapLong,在Jdk8中基于CAS扩展出来的方法有getAndAddIntgetAndAddLonggetAndSetIntgetAndSetLonggetAndSetObject,它们的作用都是:通过CAS设置新的值,返回旧的值。

线程的挂起和恢复#

unpark#

  • public native void unpark(Object thread);

释放被park创建的在一个线程上的阻塞。这个方法也可以被使用来终止一个先前调用park导致的阻塞。这个操作是不安全的,因此必须保证线程是存活的(thread has not been destroyed)。从Java代码中判断一个线程是否存活的是显而易见的,但是从native代码中这机会是不可能自动完成的。

park#

  • public native void park(boolean isAbsolute, long time);

阻塞当前线程直到一个unpark方法出现(被调用)、一个用于unpark方法已经出现过(在此park方法调用之前已经调用过)、线程被中断或者time时间到期(也就是阻塞超时)。在time非零的情况下,如果isAbsolute为true,time是相对于新纪元之后的毫秒,否则time表示纳秒。这个方法执行时也可能不合理地返回(没有具体原因)。并发包java.util.concurrent中的框架对线程的挂起操作被封装在LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe#park()方法。

内存屏障#

内存屏障相关的方法是在Jdk8添加的。内存屏障相关的知识可以先自行查阅。

loadFence#

  • public native void loadFence();

在该方法之前的所有读操作,一定在load屏障之前执行完成。

storeFence#

  • public native void storeFence();

在该方法之前的所有写操作,一定在store屏障之前执行完成

fullFence#

  • public native void fullFence();

在该方法之前的所有读写操作,一定在full屏障之前执行完成,这个内存屏障相当于上面两个(load屏障和store屏障)的合体功能。

其他#

getLoadAverage#

  • public native int getLoadAverage(double[] loadavg, int nelems);

获取系统的平均负载值,loadavg这个double数组将会存放负载值的结果,nelems决定样本数量,nelems只能取值为1到3,分别代表最近1、5、15分钟内系统的平均负载。如果无法获取系统的负载,此方法返回-1,否则返回获取到的样本数量(loadavg中有效的元素个数)。实验中这个方法一直返回-1,其实完全可以使用JMX中的相关方法替代此方法。

throwException#

  • public native void throwException(Throwable ee);

绕过检测机制直接抛出异常。

作者: throwable 、只会一点java 

出处:https://www.cnblogs.com/throwable/p/9139947.html、 https://www.cnblogs.com/dennyzhangdd/p/7230012.html

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

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

相关文章

让AMD在中国发声 APU14技术创新大会首次在华召开

今日&#xff0c;AMD一年一度的开发者峰会“APU2014”在北京拉开帷幕&#xff0c;这也是AMD首次在美国之外的城市举办该活动。AMD全球副总裁、大中华区董事总经理潘晓明表示&#xff0c;大中华区是AMD重要的战略区域&#xff0c;AMD希望通过本次活动在中国制造巨大的声音&#…

Python已成美国顶尖高校中最受欢迎的入门编程语言

在最近的一份调查中显示&#xff0c;美国top高校中&#xff0c;Python已经成为教授计算机科学入门课程方面最受欢迎的语言。其中Top10 CS系中有8所使用Python&#xff0c;Top39 CS系中有24所&#xff0c;在入门课程中教授Python&#xff0c;可见其实用性的认可度很高。在我写下…

源码阅读 AtomicInteger

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 AtomicInteger 原子整数 可以原子更新的int值。 用于原子递增计数器等应用程序中&#xff0c;不能用作java.lang.Integer的替换。 扩展…

A饭福利,AMD Mantle API获众多游戏开发商青睐!

摘要&#xff1a;Videocardz整理了一份2014年—2015年支持AMD Mantle游戏列表&#xff0c;并公布了游戏开发商及游戏引擎的名称。已发布且支持Mantle的游戏主要有《战地4》、《神偷4》、《植物大战僵尸&#xff1a;花园战争》以及《狙击精英3》这四款。 现如今&#xff0c;越来…

linux 安装 maven 、解决:bash: mvn: command not found

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1、安装 wget 命令: yum -y install wget 2、下载maven安装包 wget http://mirrors.cnnic.cn/apache/maven/maven-3/3.5.4/binaries/a…

软件工程师必学的9件事

本文是html5tricks原创翻译&#xff0c;转载请看清文末的转载要求&#xff0c;谢谢合作&#xff01; 三年前&#xff0c;我还在巴塞罗那的神经科学实验室工作&#xff0c;忙着研究脑电波、教授心理学上的认知系统课程。而今天&#xff0c;我以设计和写软件为生。 你或许会满头…

Linux 的 chmod 命令,对一个目录及其子目录所有文件添加权限

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 对一个目录及其子目录所有文件添加权限 命令&#xff1a; chmod 777 -R ./html 给予html目录下可读可写可操作权限。 或者 chmod -R…

Linux 下压缩与解压.zip 和 .rar

1)对于.ziplinux下提供了zip和unzip程序&#xff0c;zip是压缩程序&#xff0c;unzip是解压程序。它们的参数选项很多&#xff0c;可用命令zip -help和unzip -help查看&#xff0c;这里只做简单介绍&#xff0c;举例说明一下其用法&#xff1a;# zip test.zip test.jpg test.pn…

优秀的程序员VS糟糕的程序员

优秀的程序员和一般的程序员差别在哪里&#xff1f;怎么才能成为优秀的程序员&#xff1f;我们选择了这个职业就要把他做好&#xff01; 优秀的程序员&#xff1a; 1、逻辑能力很强&#xff0c;这也是解决问题的关键。 2、分析能力。可以很好的解决复杂问题。 3、事情做得专…

图解 Java 常用数据结构

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程. 主要基于jdk8, 可能会有些特性与…

程序员生存定律--使人生永动的势能

程序员生存定律这系列的目录在这里&#xff1a;程序员生存定律--目录 喜欢从头瞄的&#xff0c;可以移步。 ------------------------------------------------------------------------------- 这篇说的是精神&#xff0c;比较务虚&#xff0c;不感兴趣的可以略过。 在国内有…

int 和 Integer 的区别

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1、Integer是int的包装类&#xff0c;int则是java的一种基本数据类型 2、Integer变量必须实例化后才能使用&#xff0c;而int变量不需要…

度量术语之二:应用类和开发类生产率(实际度量案例)

一个令人震惊的事实是连生产率这种常见度量数据都没有一个简单的定义。连我们日常经常用到的公式&#xff1a;生产率工作产品/工作量&#xff08;工作产品可以是代码行&#xff0c;功能点&#xff0c;也可以是任何可以计数的东西&#xff0c;比如文档页数&#xff09;都是错误的…

注解 @ModelAttribute 运用详细介绍

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。1.ModelAttribute注释方法   例子&#xff08;1&#xff09;&#xff0c;&#xff08;2&#xff09;&#xff0c;&#xff08;3&#x…

编程语言 IDE 对比

IDE是集成开发环境的英文缩写&#xff0c;所谓集成开发环境&#xff0c;就是将你在开发过程中所需要的工具或功能集成到了一起&#xff0c;比如代码编写、分析、编译、调试等功能&#xff0c;从而最大化地提高开发者的工作效率。每种编程语言都有一些特定的IDE&#xff0c;本文…

强制更新 maven 缓存

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 mvn dependency:purge-local-repository

程序员为什么那么难升职

一个有趣的现象是老程序员很难升职&#xff0c;如果你因为3K工资太低而要辞掉工作&#xff0c;你的上司宁可去外面找一个5K工资的新人&#xff0c;也不会来挽留你。那么程序员为什么那么难升职&#xff0c;这里总结了几点。你上司的问题你晋升困难&#xff0c;最大的主观原因在…

Docker 安装 Redis (Redis 配置)

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 获取 redis 镜像 docker pull redis 不加版本号默认获取最新版本&#xff0c;也可以使用 docker search redis 查看镜像来源 查看本地镜像…

百度首席科学家 Andrew Ng谈深度学习的挑战和未来

摘要&#xff1a;7月7日上午&#xff0c;百度首席科学家Andrew Ng应邀做客中国科学院自动化研究所并做了《Deep Learning&#xff1a;Overview and trends》的学术报告。 【编者按】人工智能被认为是下一个互联网大事件&#xff0c;当下&#xff0c;谷歌、微软、百度等知名的高…

Linux 安装 jdk ( 两种方式 )

安装jdk有两种方法&#xff1a;手动安装 yum安装。 方式一&#xff1a; yum安装 1、查询要安装jdk的版本, 命令&#xff1a;yum -y list java* 2、安装jdk1.8 yum install -y java-1.8.0-openjdk.x86_64 3、查询jdk版本&#xff1a;java -version 这样就安装成功了。默认…