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.pswp.cn/news/447481.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/447481.shtml
英文地址,請注明出處:http://en.pswp.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 這樣就安裝成功了。默認…